Merge "Introduce system shortcut key unit tests"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 65b2511..f8aa7e9 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -17,17 +17,12 @@
tests/
tools/
bpfmt = -d
+
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
-
-owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
-
-shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 40a3c92..448ee61 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -316,8 +316,6 @@
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
- waitForBroadcastIdle();
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -680,8 +678,6 @@
}
private void stopUser(int userId, boolean force) throws RemoteException {
- waitForBroadcastIdle();
-
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@Override
@@ -886,8 +882,4 @@
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
return oldValue;
}
-
- private void waitForBroadcastIdle() {
- ShellHelper.runShellCommand("am wait-for-broadcast-idle");
- }
}
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index b8bd8b4..0f3a068 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
@@ -52,12 +52,20 @@
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
<option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <!-- SimpleperfListener related arguments -->
+ <option name="instrumentation-arg" key="report" value="true" />
+ <option name="instrumentation-arg" key="arguments" value="""" />
+ <option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
+ <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" />
+ <option name="instrumentation-arg" key="symbols_to_report" value=""android::SurfaceFlinger::commit(long, long, long)"" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/data/local/tmp/SurfaceFlingerPerfTests" />
<!-- Needed for pulling the collected trace config on to the host -->
<option name="pull-pattern-keys" value="perfetto_file_path" />
+ <option name="pull-pattern-keys" value="simpleperf_file_path" />
</metrics_collector>
</configuration>
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index 347cb78..8efe48d 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -48,6 +48,5 @@
while (state.keepRunning()) {
// Do Something
}
-
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 2da00c7..6d5c160 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -274,24 +274,19 @@
public void onBootPhase(int phase) {
mBootPhase = phase;
- if (PHASE_SYSTEM_SERVICES_READY == phase) {
- mConfigObserver.start();
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- setupEverything();
- } else if (PHASE_BOOT_COMPLETED == phase) {
- if (!mExemptListLoaded) {
- synchronized (mLock) {
- try {
- mExemptedApps =
- new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
- } catch (RemoteException e) {
- // Shouldn't happen.
- Slog.wtf(TAG, e);
- }
- mExemptListLoaded = true;
- }
- }
+ switch (phase) {
+ case PHASE_SYSTEM_SERVICES_READY:
+ mConfigObserver.start();
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ onBootPhaseSystemServicesReady();
+ break;
+ case PHASE_THIRD_PARTY_APPS_CAN_START:
+ onBootPhaseThirdPartyAppsCanStart();
+ break;
+ case PHASE_BOOT_COMPLETED:
+ onBootPhaseBootCompleted();
+ break;
}
}
@@ -403,10 +398,9 @@
final ArraySet<String> added = new ArraySet<>();
try {
mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ mExemptListLoaded = true;
} catch (RemoteException e) {
// Shouldn't happen.
- Slog.wtf(TAG, e);
- return;
}
for (int i = mExemptedApps.size() - 1; i >= 0; --i) {
@@ -695,17 +689,11 @@
/** Perform long-running and/or heavy setup work. This should be called off the main thread. */
private void setupHeavyWork() {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ return;
+ }
synchronized (mLock) {
loadInstalledPackageListLocked();
- if (mBootPhase >= PHASE_BOOT_COMPLETED && !mExemptListLoaded) {
- try {
- mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
- } catch (RemoteException e) {
- // Shouldn't happen.
- Slog.wtf(TAG, e);
- }
- mExemptListLoaded = true;
- }
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
@@ -726,18 +714,57 @@
}
}
- private void setupEverything() {
+ private void onBootPhaseSystemServicesReady() {
if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
return;
}
synchronized (mLock) {
registerListeners();
mCurrentBatteryLevel = getCurrentBatteryLevel();
+ }
+ }
+
+ private void onBootPhaseThirdPartyAppsCanStart() {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
mHandler.post(this::setupHeavyWork);
mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
}
}
+ private void onBootPhaseBootCompleted() {
+ if (mBootPhase < PHASE_BOOT_COMPLETED || !mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mExemptListLoaded) {
+ try {
+ mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ mExemptListLoaded = true;
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ }
+ }
+ }
+
+ private void setupEverything() {
+ if (!mIsEnabled) {
+ return;
+ }
+ if (mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
+ onBootPhaseSystemServicesReady();
+ }
+ if (mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
+ onBootPhaseThirdPartyAppsCanStart();
+ }
+ if (mBootPhase >= PHASE_BOOT_COMPLETED) {
+ onBootPhaseBootCompleted();
+ }
+ }
+
private void tearDownEverything() {
if (mIsEnabled) {
return;
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9add310e..4b090f5 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -25,6 +25,7 @@
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -338,7 +339,7 @@
mReader.beginArray();
while (mReader.hasNext()) {
int type = 0;
- int[] data = null;
+ IntStream data = null;
mReader.beginObject();
while (mReader.hasNext()) {
String name = mReader.nextName();
@@ -347,8 +348,7 @@
type = readInt();
break;
case "data":
- data = readIntList().stream()
- .mapToInt(Integer::intValue).toArray();
+ data = readIntList().stream().mapToInt(Integer::intValue);
break;
default:
consumeRemainingElements();
@@ -359,7 +359,9 @@
}
mReader.endObject();
if (data != null) {
- configuration.put(type, data);
+ final int[] existing = configuration.get(type);
+ configuration.put(type, existing == null ? data.toArray()
+ : IntStream.concat(IntStream.of(existing), data).toArray());
}
}
mReader.endArray();
diff --git a/core/api/current.txt b/core/api/current.txt
index 1cd8253..7ed1a43 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11523,6 +11523,7 @@
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+ field public static final int REQUESTED_PERMISSION_IMPLICIT = 4; // 0x4
field public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 65536; // 0x10000
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
@@ -32262,6 +32263,7 @@
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean isUserUnlocked();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserUnlocked(android.os.UserHandle);
+ method public boolean isUserVisible();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.MODIFY_QUIET_MODE"}, conditional=true) public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
method @Deprecated public boolean setRestrictionsChallenge(String);
@@ -49228,6 +49230,10 @@
}
public final class PixelCopy {
+ method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
method public static void request(@NonNull android.view.SurfaceView, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
method public static void request(@NonNull android.view.SurfaceView, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
method public static void request(@NonNull android.view.Surface, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
@@ -49242,10 +49248,21 @@
field public static final int SUCCESS = 0; // 0x0
}
+ public static final class PixelCopy.CopyResult {
+ method @NonNull public android.graphics.Bitmap getBitmap();
+ method public int getStatus();
+ }
+
public static interface PixelCopy.OnPixelCopyFinishedListener {
method public void onPixelCopyFinished(int);
}
+ public static final class PixelCopy.Request {
+ method public void request();
+ method @NonNull public android.view.PixelCopy.Request setDestinationBitmap(@Nullable android.graphics.Bitmap);
+ method @NonNull public android.view.PixelCopy.Request setSourceRect(@Nullable android.graphics.Rect);
+ }
+
public final class PointerIcon implements android.os.Parcelable {
method @NonNull public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float);
method public int describeContents();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 09d4ba6..ddbccfa 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -824,6 +824,8 @@
}
public abstract class PackageManager {
+ method public abstract void addCrossProfileIntentFilter(@NonNull android.content.IntentFilter, int, int, int);
+ method public abstract void clearCrossProfileIntentFilters(int);
method @Deprecated @Nullable public final String getContentCaptureServicePackageName();
method @Nullable public String getDefaultTextClassifierPackageName();
method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 90b7828..e86d2f3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -43,9 +43,9 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -72,10 +72,7 @@
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2634,7 +2631,7 @@
*/
@Override
public final IBinder onBind(Intent intent) {
- return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
+ return new IAccessibilityServiceClientWrapper(this, getMainExecutor(), new Callbacks() {
@Override
public void onServiceConnected() {
AccessibilityService.this.dispatchServiceConnected();
@@ -2751,30 +2748,12 @@
*
* @hide
*/
- public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
- implements HandlerCaller.Callback {
- private static final int DO_INIT = 1;
- private static final int DO_ON_INTERRUPT = 2;
- private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
- private static final int DO_ON_GESTURE = 4;
- private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
- private static final int DO_ON_KEY_EVENT = 6;
- private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
- private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
- private static final int DO_GESTURE_COMPLETE = 9;
- private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
- private static final int DO_ON_FINGERPRINT_GESTURE = 11;
- private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
- private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
- private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
- private static final int DO_CREATE_IME_SESSION = 15;
- private static final int DO_SET_IME_SESSION_ENABLED = 16;
- private static final int DO_START_INPUT = 19;
-
- private final HandlerCaller mCaller;
+ public static class IAccessibilityServiceClientWrapper extends
+ IAccessibilityServiceClient.Stub {
private final Callbacks mCallback;
private final Context mContext;
+ private final Executor mExecutor;
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -2793,103 +2772,199 @@
@Nullable
CancellationGroup mCancellationGroup = null;
+ public IAccessibilityServiceClientWrapper(Context context, Executor executor,
+ Callbacks callback) {
+ mCallback = callback;
+ mContext = context;
+ mExecutor = executor;
+ }
+
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
mContext = context;
- mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
+ mExecutor = new HandlerExecutor(new Handler(looper));
}
public void init(IAccessibilityServiceConnection connection, int connectionId,
IBinder windowToken) {
- Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
- connection, windowToken);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ mConnectionId = connectionId;
+ if (connection != null) {
+ AccessibilityInteractionClient.getInstance(mContext).addConnection(
+ mConnectionId, connection, /*initializeCache=*/true);
+ if (mContext != null) {
+ try {
+ connection.setAttributionTag(mContext.getAttributionTag());
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting attributionTag", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ mCallback.init(mConnectionId, windowToken);
+ mCallback.onServiceConnected();
+ } else {
+ AccessibilityInteractionClient.getInstance(mContext)
+ .clearCache(mConnectionId);
+ AccessibilityInteractionClient.getInstance(mContext).removeConnection(
+ mConnectionId);
+ mConnectionId = AccessibilityInteractionClient.NO_ID;
+ mCallback.init(AccessibilityInteractionClient.NO_ID, null);
+ }
+ return;
+ });
}
public void onInterrupt() {
- Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onInterrupt();
+ }
+ });
}
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
- Message message = mCaller.obtainMessageBO(
- DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (event != null) {
+ // Send the event to AccessibilityCache via AccessibilityInteractionClient
+ AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
+ event, mConnectionId);
+ if (serviceWantsEvent
+ && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
+ // Send the event to AccessibilityService
+ mCallback.onAccessibilityEvent(event);
+ }
+ }
+ return;
+ });
}
@Override
public void onGesture(AccessibilityGestureEvent gestureInfo) {
- Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onGesture(gestureInfo);
+ }
+ return;
+ });
}
public void clearAccessibilityCache() {
- Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
+ return;
+ });
}
@Override
public void onKeyEvent(KeyEvent event, int sequence) {
- Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ try {
+ IAccessibilityServiceConnection connection = AccessibilityInteractionClient
+ .getInstance(mContext).getConnection(mConnectionId);
+ if (connection != null) {
+ final boolean result = mCallback.onKeyEvent(event);
+ try {
+ connection.setOnKeyEventResult(result, sequence);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
+ }
+ return;
+ });
}
/** Magnification changed callbacks for different displays */
public void onMagnificationChanged(int displayId, @NonNull Region region,
MagnificationConfig config) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = region;
- args.arg2 = config;
- args.argi1 = displayId;
-
- final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onMagnificationChanged(displayId, region, config);
+ }
+ return;
+ });
}
public void onSoftKeyboardShowModeChanged(int showMode) {
- final Message message =
- mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onSoftKeyboardShowModeChanged(showMode);
+ }
+ return;
+ });
}
public void onPerformGestureResult(int sequence, boolean successfully) {
- Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
- successfully ? 1 : 0);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onPerformGestureResult(sequence, successfully);
+ }
+ return;
+ });
}
public void onFingerprintCapturingGesturesChanged(boolean active) {
- mCaller.sendMessage(mCaller.obtainMessageI(
- DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintCapturingGesturesChanged(active);
+ }
+ return;
+ });
}
public void onFingerprintGesture(int gesture) {
- mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintGesture(gesture);
+ }
+ return;
+ });
}
/** Accessibility button clicked callbacks for different displays */
public void onAccessibilityButtonClicked(int displayId) {
- final Message message = mCaller.obtainMessageI(DO_ACCESSIBILITY_BUTTON_CLICKED,
- displayId);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onAccessibilityButtonClicked(displayId);
+ }
+ return;
+ });
}
public void onAccessibilityButtonAvailabilityChanged(boolean available) {
- final Message message = mCaller.obtainMessageI(
- DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onAccessibilityButtonAvailabilityChanged(available);
+ }
+ return;
+ });
}
/** This is called when the system action list is changed. */
public void onSystemActionsChanged() {
- mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onSystemActionsChanged();
+ }
+ return;
+ });
}
/** This is called when an app requests ime sessions or when the service is enabled. */
public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
- final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.createImeSession(callback);
+ }
+ });
}
/**
@@ -2905,8 +2980,12 @@
Log.w(LOG_TAG, "Session is already finished: " + session);
return;
}
- mCaller.sendMessage(mCaller.obtainMessageIO(
- DO_SET_IME_SESSION_ENABLED, enabled ? 1 : 0, ls));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ ls.setEnabled(enabled);
+ }
+ return;
+ });
} catch (ClassCastException e) {
Log.w(LOG_TAG, "Incoming session not of correct type: " + session, e);
}
@@ -2938,213 +3017,29 @@
Log.e(LOG_TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
- final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */,
- connection, editorInfo, mCancellationGroup, restarting ? 1 : 0,
- 0 /* unused */);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final RemoteAccessibilityInputConnection ic = connection == null ? null
+ : new RemoteAccessibilityInputConnection(
+ connection, mCancellationGroup);
+ editorInfo.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
+ mCallback.startInput(ic, editorInfo, restarting);
+ }
+ });
}
@Override
public void onMotionEvent(MotionEvent event) {
- final Message message = PooledLambda.obtainMessage(
- Callbacks::onMotionEvent, mCallback, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ mCallback.onMotionEvent(event);
+ });
}
@Override
public void onTouchStateChanged(int displayId, int state) {
- final Message message = PooledLambda.obtainMessage(Callbacks::onTouchStateChanged,
- mCallback,
- displayId, state);
- mCaller.sendMessage(message);
- }
-
- @Override
- public void executeMessage(Message message) {
- switch (message.what) {
- case DO_ON_ACCESSIBILITY_EVENT: {
- AccessibilityEvent event = (AccessibilityEvent) message.obj;
- boolean serviceWantsEvent = message.arg1 != 0;
- if (event != null) {
- // Send the event to AccessibilityCache via AccessibilityInteractionClient
- AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
- event, mConnectionId);
- if (serviceWantsEvent
- && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
- // Send the event to AccessibilityService
- mCallback.onAccessibilityEvent(event);
- }
- // Make sure the event is recycled.
- try {
- event.recycle();
- } catch (IllegalStateException ise) {
- /* ignore - best effort */
- }
- }
- return;
- }
- case DO_ON_INTERRUPT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onInterrupt();
- }
- return;
- }
- case DO_INIT: {
- mConnectionId = message.arg1;
- SomeArgs args = (SomeArgs) message.obj;
- IAccessibilityServiceConnection connection =
- (IAccessibilityServiceConnection) args.arg1;
- IBinder windowToken = (IBinder) args.arg2;
- args.recycle();
- if (connection != null) {
- AccessibilityInteractionClient.getInstance(mContext).addConnection(
- mConnectionId, connection, /*initializeCache=*/true);
- if (mContext != null) {
- try {
- connection.setAttributionTag(mContext.getAttributionTag());
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Error while setting attributionTag", re);
- re.rethrowFromSystemServer();
- }
- }
- mCallback.init(mConnectionId, windowToken);
- mCallback.onServiceConnected();
- } else {
- AccessibilityInteractionClient.getInstance(mContext)
- .clearCache(mConnectionId);
- AccessibilityInteractionClient.getInstance(mContext).removeConnection(
- mConnectionId);
- mConnectionId = AccessibilityInteractionClient.NO_ID;
- mCallback.init(AccessibilityInteractionClient.NO_ID, null);
- }
- return;
- }
- case DO_ON_GESTURE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onGesture((AccessibilityGestureEvent) message.obj);
- }
- return;
- }
- case DO_CLEAR_ACCESSIBILITY_CACHE: {
- AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
- return;
- }
- case DO_ON_KEY_EVENT: {
- KeyEvent event = (KeyEvent) message.obj;
- try {
- IAccessibilityServiceConnection connection = AccessibilityInteractionClient
- .getInstance(mContext).getConnection(mConnectionId);
- if (connection != null) {
- final boolean result = mCallback.onKeyEvent(event);
- final int sequence = message.arg1;
- try {
- connection.setOnKeyEventResult(result, sequence);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- } finally {
- // Make sure the event is recycled.
- try {
- event.recycle();
- } catch (IllegalStateException ise) {
- /* ignore - best effort */
- }
- }
- return;
- }
- case DO_ON_MAGNIFICATION_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final SomeArgs args = (SomeArgs) message.obj;
- final Region region = (Region) args.arg1;
- final MagnificationConfig config = (MagnificationConfig) args.arg2;
- final int displayId = args.argi1;
- args.recycle();
- mCallback.onMagnificationChanged(displayId, region, config);
- }
- return;
- }
- case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final int showMode = (int) message.arg1;
- mCallback.onSoftKeyboardShowModeChanged(showMode);
- }
- return;
- }
- case DO_GESTURE_COMPLETE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final boolean successfully = message.arg2 == 1;
- mCallback.onPerformGestureResult(message.arg1, successfully);
- }
- return;
- }
- case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
- }
- return;
- }
- case DO_ON_FINGERPRINT_GESTURE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onFingerprintGesture(message.arg1);
- }
- return;
- }
- case DO_ACCESSIBILITY_BUTTON_CLICKED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onAccessibilityButtonClicked(message.arg1);
- }
- return;
- }
- case DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final boolean available = (message.arg1 != 0);
- mCallback.onAccessibilityButtonAvailabilityChanged(available);
- }
- return;
- }
- case DO_ON_SYSTEM_ACTIONS_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onSystemActionsChanged();
- }
- return;
- }
- case DO_CREATE_IME_SESSION: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- IAccessibilityInputMethodSessionCallback callback =
- (IAccessibilityInputMethodSessionCallback) message.obj;
- mCallback.createImeSession(callback);
- }
- return;
- }
- case DO_SET_IME_SESSION_ENABLED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- AccessibilityInputMethodSession session =
- (AccessibilityInputMethodSession) message.obj;
- session.setEnabled(message.arg1 != 0);
- }
- return;
- }
- case DO_START_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final SomeArgs args = (SomeArgs) message.obj;
- final IRemoteAccessibilityInputConnection connection =
- (IRemoteAccessibilityInputConnection) args.arg2;
- final EditorInfo info = (EditorInfo) args.arg3;
- final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
- final boolean restarting = args.argi5 == 1;
- final RemoteAccessibilityInputConnection ic = connection == null ? null
- : new RemoteAccessibilityInputConnection(
- connection, cancellationGroup);
- info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
- mCallback.startInput(ic, info, restarting);
- args.recycle();
- }
- return;
- }
- default:
- Log.w(LOG_TAG, "Unknown message type " + message.what);
- }
+ mExecutor.execute(() -> {
+ mCallback.onTouchStateChanged(displayId, state);
+ });
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e25e374..449729e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5124,7 +5124,7 @@
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
try {
- listener.init(mContext, executor, this);
+ listener.init(mContext, executor);
getService().registerProcessObserver(listener.mObserver);
// Notify upon first registration.
executor.execute(() ->
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c26..f17d5b7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -378,7 +381,8 @@
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents) {
- return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+ return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -388,8 +392,20 @@
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @return List of running tasks that can be filtered by visibility and displayId in recents
+ * and keep intent extra.
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ * @hide
+ */
+ public List<ActivityManager.RunningTaskInfo> getTasks(
+ int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
try {
- return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+ return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+ displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b383d7d..6b3dc82 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -107,6 +107,7 @@
import android.net.Proxy;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.wifi.WifiFrameworkInitializer;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.BluetoothServiceManager;
@@ -7900,6 +7901,8 @@
BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
BinderCallsStats.startForBluetooth(context); });
+ WifiFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
+ BinderCallsStats.startForWifi(context); });
}
private void purgePendingResources() {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 57dacd0..877e7d3 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,11 +263,6 @@
// After orientation change, the onResume can come in before the top Activity has
// left, so if the Activity is not top, wait a second for the top Activity to exit.
if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else {
@@ -276,11 +271,6 @@
public void run() {
if (mEnterTransitionCoordinator == null ||
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/app/GameManagerInternal.java b/core/java/android/app/GameManagerInternal.java
new file mode 100644
index 0000000..c8ff2a8
--- /dev/null
+++ b/core/java/android/app/GameManagerInternal.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * Game manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+
+public abstract class GameManagerInternal {
+ /**
+ * Used by the CompatModePackages to read game's compat scaling override.
+ */
+ public abstract float getResolutionScalingFactor(String packageName, int userId);
+}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 57d868b..ca20648 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -16,16 +16,17 @@
package android.app;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -39,20 +40,21 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public abstract class HomeVisibilityListener {
- private Context mContext;
- private ActivityManager mActivityManager;
+ private ActivityTaskManager mActivityTaskManager;
private Executor mExecutor;
+ private int mMaxScanTasksForHomeVisibility;
/** @hide */
android.app.IProcessObserver.Stub mObserver;
/** @hide */
boolean mIsHomeActivityVisible;
/** @hide */
- void init(Context context, Executor executor, ActivityManager activityManager) {
- mContext = context;
- mActivityManager = activityManager;
- mIsHomeActivityVisible = isHomeActivityVisible();
+ void init(Context context, Executor executor) {
+ mActivityTaskManager = ActivityTaskManager.getInstance();
mExecutor = executor;
+ mMaxScanTasksForHomeVisibility = context.getResources().getInteger(
+ com.android.internal.R.integer.config_maxScanTasksForHomeVisibility);
+ mIsHomeActivityVisible = isHomeActivityVisible();
}
/**
@@ -91,22 +93,21 @@
}
private boolean isHomeActivityVisible() {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
- if (tasks == null || tasks.isEmpty()) {
+ List<ActivityManager.RunningTaskInfo> tasksTopToBottom = mActivityTaskManager.getTasks(
+ mMaxScanTasksForHomeVisibility, /* filterOnlyVisibleRecents= */ true,
+ /* keepIntentExtra= */ false, DEFAULT_DISPLAY);
+ if (tasksTopToBottom == null || tasksTopToBottom.isEmpty()) {
return false;
}
- String top = tasks.get(0).topActivity.getPackageName();
- if (top == null) {
- return false;
+ for (int i = 0, taskSize = tasksTopToBottom.size(); i < taskSize; ++i) {
+ ActivityManager.RunningTaskInfo task = tasksTopToBottom.get(i);
+ if (!task.isVisible()
+ || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
+ continue;
+ }
+ return task.getActivityType() == ACTIVITY_TYPE_HOME;
}
-
- // We can assume that the screen is idle if the home application is in the foreground.
- ComponentName defaultHomeComponent = mContext.getPackageManager()
- .getHomeActivities(new ArrayList<>());
- if (defaultHomeComponent == null) return false;
-
- String defaultHomePackage = defaultHomeComponent.getPackageName();
- return Objects.equals(top, defaultHomePackage);
+ return false;
}
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3459d0e..6576a1a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
- boolean keepIntentExtra);
+ boolean keepIntentExtra, int displayId);
void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
int flags, in Bundle options);
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index e60a74a..481e7b0 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -31,4 +31,6 @@
void setGameState(String packageName, in GameState gameState, int userId);
GameModeInfo getGameModeInfo(String packageName, int userId);
void setGameServiceProvider(String packageName);
+ void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId);
+ float getResolutionScalingFactor(String packageName, int gameMode, int userId);
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 58ddd49..13934e5 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -310,6 +310,21 @@
public static final String MODULE_TELEPHONY = "telephony";
/**
+ * Constants that affect retries when the process is unable to write the property.
+ * The first constant is the number of times the process will attempt to set the
+ * property. The second constant is the delay between attempts.
+ */
+
+ /**
+ * Wait 200ms between retry attempts and the retry limit is 5. That gives a total possible
+ * delay of 1s, which should be less than ANR timeouts. The goal is to have the system crash
+ * because the property could not be set (which is a condition that is easily recognized) and
+ * not crash because of an ANR (which can be confusing to debug).
+ */
+ private static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
+ private static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
+
+ /**
* Construct a system property that matches the rules described above. The module is
* one of the permitted values above. The API is a string that is a legal Java simple
* identifier. The api is modified to conform to the system property style guide by
@@ -670,7 +685,33 @@
}
}
}
- SystemProperties.set(name, Long.toString(val));
+ RuntimeException failure = null;
+ for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
+ try {
+ SystemProperties.set(name, Long.toString(val));
+ if (attempt > 0) {
+ // This log is not guarded. Based on known bug reports, it should
+ // occur once a week or less. The purpose of the log message is to
+ // identify the retries as a source of delay that might be otherwise
+ // be attributed to the cache itself.
+ Log.w(TAG, "Nonce set after " + attempt + " tries");
+ }
+ return;
+ } catch (RuntimeException e) {
+ if (failure == null) {
+ failure = e;
+ }
+ try {
+ Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
+ } catch (InterruptedException x) {
+ // Ignore this exception. The desired delay is only approximate and
+ // there is no issue if the sleep sometimes terminates early.
+ }
+ }
+ }
+ // This point is reached only if SystemProperties.set() fails at least once.
+ // Rethrow the first exception that was received.
+ throw failure;
}
// Set the nonce in a static context. No handle is available.
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8be2b48..e3bca9c 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -103,7 +103,6 @@
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
- private SparseIntArray mColorMapping = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
private long mLastInflatedRemoteViewsId = -1;
@@ -900,11 +899,19 @@
* {@link android.R.color#system_neutral1_500}.
*/
public void setColorResources(@NonNull SparseIntArray colorMapping) {
- if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+ if (mColorResources != null
+ && isSameColorMapping(mColorResources.getColorMapping(), colorMapping)) {
return;
}
- mColorMapping = colorMapping.clone();
- mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
+ setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
+ }
+
+ /** @hide **/
+ public void setColorResources(RemoteViews.ColorResources colorResources) {
+ if (colorResources == mColorResources) {
+ return;
+ }
+ mColorResources = colorResources;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
@@ -934,7 +941,6 @@
public void resetColorResources() {
if (mColorResources != null) {
mColorResources = null;
- mColorMapping = null;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 0c171ad..da486ee 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -19,7 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Process.myUserHandle;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_DATABASE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -278,7 +278,7 @@
// Return an empty cursor for all columns.
return new MatrixCursor(cursor.getColumnNames(), 0);
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "query: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -289,7 +289,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -298,13 +298,13 @@
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getType: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
try {
return mInterface.getType(uri);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -336,7 +336,7 @@
setCallingAttributionSource(original);
}
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "insert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -345,7 +345,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -358,7 +358,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "bulkInsert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -367,7 +367,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -404,7 +404,7 @@
}
}
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "applyBatch: ", authority);
+ traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -423,7 +423,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -436,7 +436,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "delete: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -445,7 +445,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -458,7 +458,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "update: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -467,7 +467,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -478,7 +478,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -488,7 +488,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -499,7 +499,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -509,7 +509,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -518,7 +518,7 @@
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "call: ", authority);
+ traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -527,7 +527,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -536,13 +536,13 @@
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getStreamTypes: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
try {
return mInterface.getStreamTypes(uri, mimeTypeFilter);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -554,7 +554,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, "r");
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openTypedAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -564,7 +564,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -582,7 +582,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "canonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -591,7 +591,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -618,7 +618,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "uncanonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -627,7 +627,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -654,7 +654,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return false;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "refresh: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -662,7 +662,7 @@
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -671,7 +671,7 @@
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "checkUriPermission: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -680,7 +680,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 26b2f07..0e0b2dc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11051,10 +11051,7 @@
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
- if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
- && !Objects.equals(this.mPackage, other.mPackage)) {
- return false;
- }
+ if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
@@ -11062,15 +11059,6 @@
}
/**
- * Return {@code true} if the component name is not null and is in the same package that this
- * intent limited to. otherwise return {@code false}.
- */
- private boolean hasPackageEquivalentComponent() {
- return mComponent != null
- && (mPackage == null || mPackage.equals(mComponent.getPackageName()));
- }
-
- /**
* Generate hash code that matches semantics of filterEquals().
*
* @return Returns the hash value of the action, data, type, class, and
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9708493..4259600 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -252,6 +252,13 @@
public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 0x00010000;
/**
+ * Flag for {@link #requestedPermissionsFlags}: the requested permission was
+ * not explicitly requested via uses-permission, but was instead implicitly
+ * requested (e.g., for version compatibility reasons).
+ */
+ public static final int REQUESTED_PERMISSION_IMPLICIT = 0x00000004;
+
+ /**
* Array of all signatures read from the package file. This is only filled
* in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
* must be signed with at least one certificate which is at position zero.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dd517c9..5839b87 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9473,6 +9473,7 @@
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
+ @TestApi
public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
@UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
@@ -9485,6 +9486,7 @@
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
+ @TestApi
public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
/**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 3dedc41..f47c1e0 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1974,10 +1974,10 @@
return true;
}
int diff = other.seq - seq;
- if (diff > 0x10000) {
+ if (Math.abs(diff) > 0x10000000) {
// If there has been a sufficiently large jump, assume the
// sequence has wrapped around.
- return false;
+ return diff < 0;
}
return diff > 0;
}
diff --git a/core/java/android/database/TEST_MAPPING b/core/java/android/database/TEST_MAPPING
new file mode 100644
index 0000000..4a7fa66
--- /dev/null
+++ b/core/java/android/database/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsDatabaseTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 0848697..6ed87fff 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -81,7 +81,7 @@
*
* @param clientProxy the proxy of the client at the service
*/
- /* package */ void setClientProxy(IContextHubClient clientProxy) {
+ /* package */ synchronized void setClientProxy(IContextHubClient clientProxy) {
Objects.requireNonNull(clientProxy, "IContextHubClient cannot be null");
if (mClientProxy != null) {
throw new IllegalStateException("Cannot change client proxy multiple times");
@@ -93,6 +93,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ this.notifyAll();
}
/**
@@ -221,8 +222,15 @@
}
/** @hide */
- public void callbackFinished() {
+ public synchronized void callbackFinished() {
try {
+ while (mClientProxy == null) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
mClientProxy.callbackFinished();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 0418a4b..87579eb 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import java.io.Serializable;
import java.lang.ref.WeakReference;
@@ -113,17 +114,21 @@
*/
private boolean mParcelledByNative;
- /*
+ /**
* Flag indicating if mParcelledData is only referenced in this bundle.
- * mParcelledData could be referenced by other bundles if mMap contains lazy values,
+ * mParcelledData could be referenced elsewhere if mMap contains lazy values,
* and bundle data is copied to another bundle using putAll or the copy constructors.
*/
boolean mOwnsLazyValues = true;
- /*
+ /** Tracks how many lazy values are referenced in mMap */
+ private int mLazyValues = 0;
+
+ /**
* As mParcelledData is set to null when it is unparcelled, we keep a weak reference to
* it to aid in recycling it. Do not use this reference otherwise.
- */
+ * Is non-null iff mMap contains lazy values.
+ */
private WeakReference<Parcel> mWeakParcelledData = null;
/**
@@ -310,7 +315,8 @@
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
- initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
+ Preconditions.checkState(mOwnsLazyValues);
+ initializeFromParcelLocked(source, /*ownsParcel*/ true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
@@ -401,11 +407,23 @@
}
}
mMap.setValueAt(i, object);
+ mLazyValues--;
+ if (mOwnsLazyValues) {
+ Preconditions.checkState(mLazyValues >= 0, "Lazy values ref count below 0");
+ // No more lazy values in mMap, so we can recycle the parcel early rather than
+ // waiting for the next GC run
+ if (mLazyValues == 0) {
+ Preconditions.checkState(mWeakParcelledData.get() != null,
+ "Parcel recycled earlier than expected");
+ recycleParcel(mWeakParcelledData.get());
+ mWeakParcelledData = null;
+ }
+ }
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
- private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
+ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean ownsParcel,
boolean parcelledByNative) {
if (isEmptyParcel(parcelledData)) {
if (DEBUG) {
@@ -437,9 +455,10 @@
map.erase();
map.ensureCapacity(count);
}
+ int numLazyValues = 0;
try {
- recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ true, mClassLoader);
+ numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ ownsParcel, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -448,14 +467,19 @@
throw e;
}
} finally {
- mMap = map;
- if (recycleParcel) {
- recycleParcel(parcelledData);
- mWeakParcelledData = null;
- } else {
- mWeakParcelledData = new WeakReference<>(parcelledData);
+ mWeakParcelledData = null;
+ if (ownsParcel) {
+ if (numLazyValues == 0) {
+ recycleParcel(parcelledData);
+ } else {
+ mWeakParcelledData = new WeakReference<>(parcelledData);
+ }
}
+
+ mLazyValues = numLazyValues;
mParcelledByNative = false;
+ mMap = map;
+ // Set field last as it is volatile
mParcelledData = null;
}
if (DEBUG) {
@@ -592,13 +616,17 @@
/**
* Removes all elements from the mapping of this Bundle.
+ * Recycles the underlying parcel if it is still present.
*/
public void clear() {
unparcel();
if (mOwnsLazyValues && mWeakParcelledData != null) {
recycleParcel(mWeakParcelledData.get());
- mWeakParcelledData = null;
}
+
+ mWeakParcelledData = null;
+ mLazyValues = 0;
+ mOwnsLazyValues = true;
mMap.clear();
}
@@ -1844,8 +1872,8 @@
// had been constructed with parcel or to make sure they trigger deserialization of the
// bundle immediately; neither of which is obvious.
synchronized (this) {
- initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
- unparcel(/* itemwise */ true);
+ mOwnsLazyValues = false;
+ initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
}
return;
}
@@ -1862,6 +1890,7 @@
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
+ mOwnsLazyValues = true;
mParcelledByNative = isNativeBundle;
mParcelledData = p;
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e5de3e1..f69d6b0 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -125,6 +125,7 @@
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
+ boolean isUserVisible(int userId);
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d553132..80201d3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -71,7 +71,6 @@
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
-import java.util.function.Supplier;
/**
* Container for a message (data and object references) that can
@@ -5242,20 +5241,20 @@
* Reads a map into {@code map}.
*
* @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
- * @param lazy Whether to populate the map with lazy {@link Supplier} objects for
+ * @param lazy Whether to populate the map with lazy {@link Function} objects for
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
* details.
- * @return whether the parcel can be recycled or not.
+ * @return a count of the lazy values in the map
* @hide
*/
- boolean readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
boolean lazy, @Nullable ClassLoader loader) {
- boolean recycle = true;
+ int lazyValues = 0;
while (size > 0) {
String key = readString();
Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
if (value instanceof LazyValue) {
- recycle = false;
+ lazyValues++;
}
if (sorted) {
map.append(key, value);
@@ -5267,7 +5266,7 @@
if (sorted) {
map.validate();
}
- return recycle;
+ return lazyValues;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8d3509c..c802e56 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2838,6 +2838,50 @@
}
/**
+ * @hide
+ */
+ public static boolean isUsersOnSecondaryDisplaysEnabled() {
+ return SystemProperties.getBoolean("fw.users_on_secondary_displays",
+ Resources.getSystem()
+ .getBoolean(R.bool.config_multiuserUsersOnSecondaryDisplays));
+ }
+
+ /**
+ * Returns whether the device allows users to run (and launch activities) on secondary displays.
+ *
+ * @return {@code false} for most devices, except automotive vehicles with passenger displays.
+ *
+ * @hide
+ */
+ public boolean isUsersOnSecondaryDisplaysSupported() {
+ return isUsersOnSecondaryDisplaysEnabled();
+ }
+
+ /**
+ * Checks if the user is visible at the moment.
+ *
+ * <p>Roughly speaking, a "visible user" is a user that can present UI on at least one display.
+ * It includes:
+ *
+ * <ol>
+ * <li>The current foreground user in the main display.
+ * <li>Current background users in secondary displays (for example, passenger users on
+ * automotive, using the display associated with their seats).
+ * <li>Profile users (in the running / started state) of other visible users.
+ * </ol>
+ *
+ * @return whether the user is visible at the moment, as defined above.
+ */
+ @UserHandleAware
+ public boolean isUserVisible() {
+ try {
+ return mService.isUserVisible(mUserId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the context user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9000899..6fc2811 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14257,7 +14257,7 @@
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE = 1;
+ public static final int DEFAULT_ENABLE_TARE = 0;
/**
* Whether to enable the TARE AlarmManager economic policy or not.
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
index d14bb48..3b23c77 100644
--- a/core/java/android/view/IWindowFocusObserver.aidl
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -16,7 +16,7 @@
package android.view;
/** {@hide} */
-interface IWindowFocusObserver
+oneway interface IWindowFocusObserver
{
void focusGained(IBinder inputToken);
void focusLost(IBinder inputToken);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index cce3e8c..a2cb1d5 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -733,7 +733,7 @@
}
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
// Only update the server side insets here.
- if (type == ITYPE_CAPTION_BAR) continue;
+ if (!CAPTION_ON_SHELL && type == ITYPE_CAPTION_BAR) continue;
InsetsSource source = mState.peekSource(type);
if (source == null) continue;
if (newState.peekSource(type) == null) {
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 44f419a..0db28d4 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -363,11 +363,11 @@
pw.print(prefix); pw.print("leash="); pw.println(leash);
pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
- pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
- pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
- pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
- pw.print(prefix); pw.print("willShowImeOnTarget="); pw.print(willShowImeOnTarget);
+ pw.print(prefix); pw.print("windowType="); pw.println(windowType);
+ pw.print(prefix); pw.print("hasAnimatingParent="); pw.println(hasAnimatingParent);
+ pw.print(prefix); pw.print("backgroundColor="); pw.println(backgroundColor);
+ pw.print(prefix); pw.print("showBackdrop="); pw.println(showBackdrop);
+ pw.print(prefix); pw.print("willShowImeOnTarget="); pw.println(willShowImeOnTarget);
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5f6c24b..84f04c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -172,7 +172,6 @@
private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
private static native long[] nativeGetPhysicalDisplayIds();
- private static native long nativeGetPrimaryPhysicalDisplayId();
private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -2395,15 +2394,6 @@
}
/**
- * Exposed to identify the correct display to apply the primary display orientation. Avoid using
- * for any other purpose.
- * @hide
- */
- public static long getPrimaryPhysicalDisplayId() {
- return nativeGetPrimaryPhysicalDisplayId();
- }
-
- /**
* @hide
*/
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index d75ff2f..5721fa6 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -422,7 +422,7 @@
public void relayout(WindowManager.LayoutParams attrs,
WindowlessWindowManager.ResizeCompleteCallback callback) {
mViewRoot.setLayoutParams(attrs, false);
- mViewRoot.setReportNextDraw(true /* syncBuffer */);
+ mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout");
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e1966a0..b2854a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -277,7 +277,7 @@
* @hide
*/
public static final boolean CAPTION_ON_SHELL =
- SystemProperties.getBoolean("persist.debug.caption_on_shell", false);
+ SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
/**
* Whether the client should compute the window frame on its own.
@@ -587,8 +587,21 @@
int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
boolean mPerformContentCapture;
-
boolean mReportNextDraw;
+ /** Set only while mReportNextDraw=true, indicating the last reason that was triggered */
+ String mLastReportNextDrawReason;
+ /** The reaason the last call to performDraw() returned false */
+ String mLastPerformDrawSkippedReason;
+ /** The reason the last call to performTraversals() returned without drawing */
+ String mLastPerformTraversalsSkipDrawReason;
+ /** The state of the local sync, if one is in progress. Can be one of the states below. */
+ int mLocalSyncState;
+
+ // The possible states of the local sync, see createSyncIfNeeded()
+ private final int LOCAL_SYNC_NONE = 0;
+ private final int LOCAL_SYNC_PENDING = 1;
+ private final int LOCAL_SYNC_RETURNED = 2;
+ private final int LOCAL_SYNC_MERGED = 3;
/**
* Set whether the draw should send the buffer to system server. When set to true, VRI will
@@ -1836,7 +1849,7 @@
mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
if (msg == MSG_RESIZED_REPORT) {
- reportNextDraw();
+ reportNextDraw("resized");
}
if (mView != null && (frameChanged || configChanged)) {
@@ -2741,6 +2754,8 @@
}
private void performTraversals() {
+ mLastPerformTraversalsSkipDrawReason = null;
+
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
@@ -2750,12 +2765,14 @@
}
if (host == null || !mAdded) {
+ mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
return;
}
mIsInTraversal = true;
mWillDrawSoon = true;
boolean cancelDraw = false;
+ String cancelReason = null;
boolean isSyncRequest = false;
boolean windowSizeMayChange = false;
@@ -3038,13 +3055,14 @@
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
== RELAYOUT_RES_CANCEL_AND_REDRAW;
+ cancelReason = "relayout";
final boolean dragResizing = mPendingDragResizing;
if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId;
if (DEBUG_BLAST) {
Log.d(mTag, "Relayout called with blastSync");
}
- reportNextDraw();
+ reportNextDraw("relayout");
mSyncBuffer = true;
isSyncRequest = true;
if (!cancelDraw) {
@@ -3147,6 +3165,7 @@
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
+ mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer";
return;
}
}
@@ -3184,6 +3203,7 @@
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
+ mLastPerformTraversalsSkipDrawReason = "oom_update_surface";
return;
}
}
@@ -3349,6 +3369,7 @@
if (mCheckIfCanDraw) {
try {
cancelDraw = mWindowSession.cancelDraw(mWindow);
+ cancelReason = "wm_sync";
if (DEBUG_BLAST) {
Log.d(mTag, "cancelDraw returned " + cancelDraw);
}
@@ -3571,19 +3592,21 @@
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
- reportNextDraw();
+ reportNextDraw("first_relayout");
}
mCheckIfCanDraw = isSyncRequest || cancelDraw;
- boolean cancelAndRedraw =
- mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync);
+ boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+ boolean cancelAndRedraw = cancelDueToPreDrawListener
+ || (cancelDraw && mDrewOnceForSync);
if (!cancelAndRedraw) {
createSyncIfNeeded();
mDrewOnceForSync = true;
}
if (!isViewVisible) {
+ mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
@@ -3595,6 +3618,9 @@
mSyncBufferCallback.onBufferReady(null);
}
} else if (cancelAndRedraw) {
+ mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
+ ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
+ : "cancel_" + cancelReason;
// Try again
scheduleTraversals();
} else {
@@ -3618,11 +3644,13 @@
if (!cancelAndRedraw) {
mReportNextDraw = false;
+ mLastReportNextDrawReason = null;
mSyncBufferCallback = null;
mSyncBuffer = false;
if (isInLocalSync()) {
mSyncGroup.markSyncReady();
mSyncGroup = null;
+ mLocalSyncState = LOCAL_SYNC_NONE;
}
}
}
@@ -3634,12 +3662,15 @@
}
final int seqId = mSyncSeqId;
+ mLocalSyncState = LOCAL_SYNC_PENDING;
mSyncGroup = new SurfaceSyncGroup(transaction -> {
+ mLocalSyncState = LOCAL_SYNC_RETURNED;
// Callback will be invoked on executor thread so post to main thread.
mHandler.postAtFrontOfQueue(() -> {
if (transaction != null) {
mSurfaceChangedTransaction.merge(transaction);
}
+ mLocalSyncState = LOCAL_SYNC_MERGED;
reportDrawFinished(seqId);
});
});
@@ -4353,9 +4384,12 @@
}
private boolean performDraw() {
+ mLastPerformDrawSkippedReason = null;
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
+ mLastPerformDrawSkippedReason = "screen_off";
return false;
} else if (mView == null) {
+ mLastPerformDrawSkippedReason = "no_root_view";
return false;
}
@@ -8458,6 +8492,21 @@
if (mTraversalScheduled) {
writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")");
}
+ writer.println(innerPrefix + "mReportNextDraw=" + mReportNextDraw);
+ if (mReportNextDraw) {
+ writer.println(innerPrefix + " (reason=" + mLastReportNextDrawReason + ")");
+ }
+ if (mLastPerformTraversalsSkipDrawReason != null) {
+ writer.println(innerPrefix + "mLastPerformTraversalsFailedReason="
+ + mLastPerformTraversalsSkipDrawReason);
+ }
+ if (mLastPerformDrawSkippedReason != null) {
+ writer.println(innerPrefix + "mLastPerformDrawFailedReason="
+ + mLastPerformDrawSkippedReason);
+ }
+ if (mLocalSyncState != LOCAL_SYNC_NONE) {
+ writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState);
+ }
writer.println(innerPrefix + "mIsAmbientMode=" + mIsAmbientMode);
writer.println(innerPrefix + "mUnbufferedInputSource="
+ Integer.toHexString(mUnbufferedInputSource));
@@ -9958,11 +10007,12 @@
}
}
- private void reportNextDraw() {
+ private void reportNextDraw(String reason) {
if (DEBUG_BLAST) {
Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
}
mReportNextDraw = true;
+ mLastReportNextDrawReason = reason;
}
/**
@@ -9975,11 +10025,12 @@
* @param syncBuffer If true, the transaction that contains the buffer from the draw should be
* sent to system to be synced. If false, VRI will not try to sync the buffer,
* but only report back that a buffer was drawn.
+ * @param reason A debug string indicating the reason for reporting the next draw
* @hide
*/
- public void setReportNextDraw(boolean syncBuffer) {
+ public void setReportNextDraw(boolean syncBuffer, String reason) {
mSyncBuffer = syncBuffer;
- reportNextDraw();
+ reportNextDraw(reason);
invalidate();
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 2c077c3..c9526fd 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -74,6 +74,9 @@
* that the listener will be immediately called. */
private boolean mWindowShown;
+ // The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw
+ private String mLastDispatchOnPreDrawCanceledReason;
+
private boolean mAlive = true;
/**
@@ -1161,6 +1164,7 @@
*/
@SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
+ mLastDispatchOnPreDrawCanceledReason = null;
boolean cancelDraw = false;
final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
if (listeners != null && listeners.size() > 0) {
@@ -1168,7 +1172,11 @@
try {
int count = access.size();
for (int i = 0; i < count; i++) {
- cancelDraw |= !(access.get(i).onPreDraw());
+ final OnPreDrawListener preDrawListener = access.get(i);
+ cancelDraw |= !(preDrawListener.onPreDraw());
+ if (cancelDraw) {
+ mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName();
+ }
}
} finally {
listeners.end();
@@ -1178,6 +1186,15 @@
}
/**
+ * @return the reason that the last call to dispatchOnPreDraw() returned true to cancel the
+ * current draw, or null if the last call did not cancel.
+ * @hide
+ */
+ final String getLastDispatchOnPreDrawCanceledReason() {
+ return mLastDispatchOnPreDrawCanceledReason;
+ }
+
+ /**
* Notifies registered listeners that the window is now shown
* @hide
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56c43e..6f69361 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -834,8 +834,8 @@
*
* <p>Default is {@code false}.
*
- * <p>The system enforcement will be added in Android 14, but some devices may start following
- * the requirement before that. The best practice for apps is to always explicitly set this
+ * <p>The system enforcement is added in Android 14, but some devices may start following the
+ * requirement before that. The best practice for apps is to always explicitly set this
* property in AndroidManifest instead of relying on the default value.
*
* <p>Example usage:
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd8..a339062 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -594,8 +594,8 @@
* SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
- public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException;
+ public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException;
public static final int MERGE_REPLACE = 0;
public static final int MERGE_APPEND = 1;
@@ -626,7 +626,7 @@
* Override this if some of the tasks can be performed async.
*/
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
return this;
}
@@ -661,9 +661,7 @@
// Constant used during async execution. It is not parcelable.
private static final Action ACTION_NOOP = new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
- }
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
};
/**
@@ -798,8 +796,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -834,8 +831,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -846,7 +842,7 @@
OnItemClickListener listener = (parent, view, position, id) -> {
RemoteResponse response = findRemoteResponseTag(view);
if (response != null) {
- response.handleViewInteraction(view, handler);
+ response.handleViewInteraction(view, params.handler);
}
};
av.setOnItemClickListener(listener);
@@ -910,8 +906,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -935,7 +930,7 @@
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -944,7 +939,7 @@
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
}
}
@@ -1025,8 +1020,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
View target = root.findViewById(viewId);
if (target == null) return;
@@ -1053,7 +1048,7 @@
&& adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
try {
((RemoteCollectionItemsAdapter) adapter).setData(
- mItems, handler, colorResources);
+ mItems, params.handler, params.colorResources);
} catch (Throwable throwable) {
// setData should never failed with the validation in the items builder, but if
// it does, catch and rethrow.
@@ -1063,8 +1058,8 @@
}
try {
- adapterView.setAdapter(
- new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+ adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems,
+ params.handler, params.colorResources));
} catch (Throwable throwable) {
// This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
// a type error.
@@ -1095,8 +1090,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1124,17 +1118,17 @@
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsInteractionHandler(handler);
+ v.setRemoteViewsInteractionHandler(params.handler);
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsOnClickHandler(handler);
+ v.setRemoteViewsOnClickHandler(params.handler);
}
}
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
copy.isAsync = true;
return copy;
@@ -1173,8 +1167,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1215,7 +1208,7 @@
target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
return;
}
- target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
+ target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
}
@Override
@@ -1253,8 +1246,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
if (!(target instanceof CompoundButton)) {
@@ -1287,7 +1279,7 @@
}
OnCheckedChangeListener onCheckedChangeListener =
- (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+ (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
button.setOnCheckedChangeListener(onCheckedChangeListener);
}
@@ -1459,8 +1451,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1517,8 +1508,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1561,8 +1551,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1598,7 +1587,13 @@
public BitmapCache(Parcel source) {
mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
- mBitmapHashes = source.readSparseIntArray();
+ mBitmapHashes = new SparseIntArray();
+ for (int i = 0; i < mBitmaps.size(); i++) {
+ Bitmap b = mBitmaps.get(i);
+ if (b != null) {
+ mBitmapHashes.put(b.hashCode(), i);
+ }
+ }
}
public int getBitmapId(Bitmap b) {
@@ -1614,7 +1609,7 @@
b = b.asShared();
}
mBitmaps.add(b);
- mBitmapHashes.put(mBitmaps.size() - 1, hash);
+ mBitmapHashes.put(hash, mBitmaps.size() - 1);
mBitmapMemory = -1;
return (mBitmaps.size() - 1);
}
@@ -1631,7 +1626,6 @@
public void writeBitmapsToParcel(Parcel dest, int flags) {
dest.writeTypedList(mBitmaps, flags);
- dest.writeSparseIntArray(mBitmapHashes);
}
public int getBitmapMemory() {
@@ -1675,12 +1669,12 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
ReflectionAction ra = new ReflectionAction(viewId, methodName,
BaseReflectionAction.BITMAP,
bitmap);
- ra.apply(root, rootParent, handler, colorResources);
+ ra.apply(root, rootParent, params);
}
@Override
@@ -1756,8 +1750,7 @@
protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
@Override
- public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1775,7 +1768,7 @@
@Override
public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return ACTION_NOOP;
@@ -2307,8 +2300,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
mRunnable.run();
}
}
@@ -2421,8 +2413,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final Context context = root.getContext();
final ViewGroup target = root.findViewById(viewId);
@@ -2451,8 +2442,7 @@
target.removeViews(nextChild, recycledViewIndex - nextChild);
}
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
- rvToApply.reapplyNestedViews(context, child, rootParent, handler,
- null /* size */, colorResources);
+ rvToApply.reapplyNestedViews(context, child, rootParent, params);
return;
}
// If we cannot recycle the views, we still remove all views in between to
@@ -2463,8 +2453,7 @@
// If we cannot recycle, insert the new view before the next recyclable child.
// Inflate nested views and add as children
- View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
- null /* size */, colorResources);
+ View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
if (mStableId != NO_ID) {
setStableId(nestedView, mStableId);
}
@@ -2477,7 +2466,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2511,8 +2500,7 @@
setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
context,
- targetVg, null /* listener */, handler, null /* size */,
- colorResources,
+ targetVg, null /* listener */, params, null /* size */,
recycled.mRoot);
final ViewTree tree = reapplyTask.doInBackground();
if (tree == null) {
@@ -2521,8 +2509,7 @@
return new RuntimeAction() {
@Override
public void apply(View root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources)
- throws ActionException {
+ ActionApplyParams params) throws ActionException {
reapplyTask.onPostExecute(tree);
if (recycledViewIndex > nextChild) {
targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
@@ -2533,23 +2520,22 @@
// If the layout id is different, still remove the children as if we recycled
// the view, to insert at the same place.
target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
- return insertNewView(context, target, handler, colorResources,
+ return insertNewView(context, target, params,
() -> targetVg.removeViews(nextChild,
recycledViewIndex - nextChild + 1));
}
}
// If we cannot recycle, simply add the view at the same available slot.
- return insertNewView(context, target, handler, colorResources, () -> {});
+ return insertNewView(context, target, params, () -> {});
}
- private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
- ColorResources colorResources, Runnable finalizeAction) {
+ private Action insertNewView(Context context, ViewTree target,
+ ActionApplyParams params, Runnable finalizeAction) {
ViewGroup targetVg = (ViewGroup) target.mRoot;
int nextChild = getNextRecyclableChild(targetVg);
final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
- null /* listener */, handler, null /* size */, colorResources,
- null /* result */);
+ null /* listener */, params, null /* size */, null /* result */);
final ViewTree tree = task.doInBackground();
if (tree == null) {
@@ -2569,8 +2555,7 @@
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
task.onPostExecute(tree);
finalizeAction.run();
targetVg.addView(task.mResult, insertIndex);
@@ -2627,8 +2612,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final ViewGroup target = root.findViewById(viewId);
if (target == null) {
@@ -2652,7 +2636,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2676,8 +2660,7 @@
}
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
if (!hasStableId(targetVg.getChildAt(i))) {
@@ -2736,8 +2719,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null || target == root) {
@@ -2752,7 +2734,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the correct view.
root.createTree();
@@ -2771,8 +2753,7 @@
parent.mChildren.remove(target);
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
parentVg.removeView(target.mRoot);
}
};
@@ -2851,8 +2832,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
@@ -2883,7 +2863,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return ACTION_NOOP;
@@ -2961,8 +2941,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -3007,8 +2986,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -3084,8 +3062,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
@@ -3230,8 +3207,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3266,8 +3242,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
// Let's traverse the viewtree and override all textColors!
Stack<View> viewsToProcess = new Stack<>();
viewsToProcess.add(root);
@@ -3317,8 +3292,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(mViewId);
if (target == null) return;
@@ -3352,8 +3326,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources)
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3404,8 +3377,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3483,8 +3456,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -5578,52 +5551,39 @@
/** @hide */
public View apply(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler));
}
/** @hide */
public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
- return applyWithTheme(context, parent, handler, applyThemeResId, null);
- }
-
- /** @hide */
- public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
- @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
- @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, null, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withThemeResId(applyThemeResId));
}
/** @hide */
public View apply(Context context, ViewGroup parent, InteractionHandler handler,
@Nullable SizeF size, @Nullable ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent, 0, colorResources);
- rvToApply.performApply(result, parent, handler, colorResources);
- return result;
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources));
}
- private View applyNestedViews(Context context, ViewGroup directParent,
- ViewGroup rootParent, InteractionHandler handler, SizeF size,
- ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, directParent, 0, colorResources);
- rvToApply.performApply(result, rootParent, handler, colorResources);
- return result;
+ /** @hide **/
+ public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
+ ActionApplyParams params) {
+ return apply(context, parent, parent, size, params);
}
- private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
- return inflateView(context, rv, parent, 0, null);
+ private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+ View result = inflateView(context, rvToApply, directParent,
+ params.applyThemeResId, params.colorResources);
+ rvToApply.performApply(result, rootParent, params);
+ return result;
}
private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
@@ -5704,7 +5664,6 @@
return applyAsync(context, parent, executor, listener, null /* handler */);
}
-
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
@@ -5723,16 +5682,19 @@
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
+
+ ActionApplyParams params = new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor);
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, null /* result */,
- true /* topLevel */).startTaskOnExecutor(executor);
+ params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
}
private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
- ColorResources colorResources, View result) {
+ OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, result, false /* topLevel */);
+ params, result, false /* topLevel */);
}
private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -5742,8 +5704,8 @@
final ViewGroup mParent;
final Context mContext;
final OnViewAppliedListener mListener;
- final InteractionHandler mHandler;
- final ColorResources mColorResources;
+ final ActionApplyParams mApplyParams;
+
/**
* Whether the remote view is the top-level one (i.e. not within an action).
*
@@ -5758,16 +5720,13 @@
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- InteractionHandler handler, ColorResources colorResources,
- View result, boolean topLevel) {
+ ActionApplyParams applyParams, View result, boolean topLevel) {
mRV = rv;
mParent = parent;
mContext = context;
mListener = listener;
- mColorResources = colorResources;
- mHandler = handler;
mTopLevel = topLevel;
-
+ mApplyParams = applyParams;
mResult = result;
}
@@ -5776,17 +5735,18 @@
protected ViewTree doInBackground(Void... params) {
try {
if (mResult == null) {
- mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
+ mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
}
mTree = new ViewTree(mResult);
+
if (mRV.mActions != null) {
int count = mRV.mActions.size();
mActions = new Action[count];
for (int i = 0; i < count && !isCancelled(); i++) {
// TODO: check if isCancelled in nested views.
- mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
- mColorResources);
+ mActions[i] = mRV.mActions.get(i)
+ .initActionAsync(mTree, mParent, mApplyParams);
}
} else {
mActions = null;
@@ -5808,10 +5768,13 @@
try {
if (mActions != null) {
- InteractionHandler handler = mHandler == null
- ? DEFAULT_INTERACTION_HANDLER : mHandler;
+
+ ActionApplyParams applyParams = mApplyParams.clone();
+ if (applyParams.handler == null) {
+ applyParams.handler = DEFAULT_INTERACTION_HANDLER;
+ }
for (Action a : mActions) {
- a.apply(viewTree.mRoot, mParent, handler, mColorResources);
+ a.apply(viewTree.mRoot, mParent, applyParams);
}
}
// If the parent of the view is has is a root, resolve the recycling.
@@ -5859,18 +5822,43 @@
* the {@link #apply(Context,ViewGroup)} call.
*/
public void reapply(Context context, View v) {
- reapply(context, v, null /* handler */);
+ reapply(context, v, null /* size */, new ActionApplyParams());
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler) {
- reapply(context, v, handler, null /* size */, null /* colorResources */);
+ reapply(context, v, null /* size */,
+ new ActionApplyParams().withInteractionHandler(handler));
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- reapply(context, v, handler, size, colorResources, true);
+ reapply(context, v, size, new ActionApplyParams()
+ .withInteractionHandler(handler).withColorResources(colorResources));
+ }
+
+ /** @hide */
+ public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
+ reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
+ }
+
+ private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+ ActionApplyParams params) {
+ reapply(context, v, rootParent, null, params, false);
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ rvToApply.performApply(v, rootParent, params);
+
+ // If the parent of the view is has is a root, resolve the recycling.
+ if (topLevel && v instanceof ViewGroup) {
+ finalizeViewRecycling((ViewGroup) v);
+ }
}
/** @hide */
@@ -5922,27 +5910,6 @@
return rvToApply;
}
- // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
- // should set it to false.
- private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
- ColorResources colorResources, boolean topLevel) {
-
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-
- rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
-
- // If the parent of the view is has is a root, resolve the recycling.
- if (topLevel && v instanceof ViewGroup) {
- finalizeViewRecycling((ViewGroup) v);
- }
- }
-
- private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
- InteractionHandler handler, SizeF size, ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
- rvToApply.performApply(v, rootParent, handler, colorResources);
- }
-
/**
* Applies all the actions to the provided view, moving as much of the task on the background
* thread as possible.
@@ -5973,19 +5940,25 @@
ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ ActionApplyParams params = new ActionApplyParams()
+ .withColorResources(colorResources)
+ .withInteractionHandler(handler)
+ .withExecutor(executor);
+
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
- context, listener, handler, colorResources, v, true /* topLevel */)
+ context, listener, params, v, true /* topLevel */)
.startTaskOnExecutor(executor);
}
- private void performApply(View v, ViewGroup parent, InteractionHandler handler,
- ColorResources colorResources) {
+ private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
+ params = params.clone();
+ if (params.handler == null) {
+ params.handler = DEFAULT_INTERACTION_HANDLER;
+ }
if (mActions != null) {
- handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
- Action a = mActions.get(i);
- a.apply(v, parent, handler, colorResources);
+ mActions.get(i).apply(v, parent, params);
}
}
}
@@ -6043,6 +6016,47 @@
}
/**
+ * Utility class to hold all the options when applying the remote views
+ * @hide
+ */
+ public class ActionApplyParams {
+
+ public InteractionHandler handler;
+ public ColorResources colorResources;
+ public Executor executor;
+ @StyleRes public int applyThemeResId;
+
+ @Override
+ public ActionApplyParams clone() {
+ return new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor)
+ .withThemeResId(applyThemeResId);
+ }
+
+ public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public ActionApplyParams withColorResources(ColorResources colorResources) {
+ this.colorResources = colorResources;
+ return this;
+ }
+
+ public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
+ this.applyThemeResId = themeResId;
+ return this;
+ }
+
+ public ActionApplyParams withExecutor(Executor executor) {
+ this.executor = executor;
+ return this;
+ }
+ }
+
+ /**
* Object allowing the modification of a context to overload the system's dynamic colors.
*
* Only colors from {@link android.R.color#system_accent1_0} to
@@ -6056,10 +6070,12 @@
// Size, in bytes, of an entry in the array of colors in an ARSC file.
private static final int ARSC_ENTRY_SIZE = 16;
- private ResourcesLoader mLoader;
+ private final ResourcesLoader mLoader;
+ private final SparseIntArray mColorMapping;
- private ColorResources(ResourcesLoader loader) {
+ private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
mLoader = loader;
+ mColorMapping = colorMapping;
}
/**
@@ -6071,6 +6087,10 @@
context.getResources().addLoaders(mLoader);
}
+ public SparseIntArray getColorMapping() {
+ return mColorMapping;
+ }
+
private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[4096];
@@ -6145,7 +6165,7 @@
ResourcesLoader colorsLoader = new ResourcesLoader();
colorsLoader.addProvider(ResourcesProvider
.loadFromTable(pfd, null /* assetsProvider */));
- return new ColorResources(colorsLoader);
+ return new ColorResources(colorsLoader, colorMapping.clone());
}
}
} finally {
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 3335c9c..79ddadb 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -16,54 +16,9 @@
package android.window;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
-
- /**
- * Called when the parent leaf Task of organized TaskFragments is changed.
- * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
- * transaction.
- *
- * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
- * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
- * bounds.
- */
- void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
-
- /**
- * Called when the {@link WindowContainerTransaction} created with
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
- *
- * @param errorCallbackToken Token set through {@link
- * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param errorBundle Bundle containing the exception, operation type and TaskFragmentInfo
- * if any. Should be created with
- * {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
- */
- void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
-
- /**
- * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
- * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
- * orginial Task. In this case, we need to notify the organizer so that it can check if the
- * Activity matches any split rule.
- *
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
- */
- void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
+ void onTransactionReady(in TaskFragmentTransaction transaction);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 765cd81..e567ced 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -16,6 +16,13 @@
package android.window;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,8 +32,11 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.SparseArray;
import android.view.RemoteAnimationDefinition;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -64,6 +74,12 @@
*/
private final Executor mExecutor;
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+ /** Map from Task id to client tokens of TaskFragments in the Task. */
+ private final SparseArray<List<IBinder>> mTaskIdToFragmentTokens = new SparseArray<>();
+ /** Map from Task id to Task configuration. */
+ private final SparseArray<Configuration> mTaskIdToConfigurations = new SparseArray<>();
+
public TaskFragmentOrganizer(@NonNull Executor executor) {
mExecutor = executor;
}
@@ -153,6 +169,27 @@
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
/**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ * @hide
+ */
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+ final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+ if (tokens == null || tokens.isEmpty()) {
+ return;
+ }
+ for (int i = tokens.size() - 1; i >= 0; i--) {
+ onTaskFragmentParentInfoChanged(tokens.get(i), parentConfig);
+ }
+ }
+
+ /**
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
@@ -186,6 +223,76 @@
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {}
+ /**
+ * Called when the transaction is ready so that the organizer can update the TaskFragments based
+ * on the changes in transaction.
+ * @hide
+ */
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
+ for (TaskFragmentTransaction.Change change : changes) {
+ // TODO(b/240519866): apply all changes in one WCT.
+ final int taskId = change.getTaskId();
+ switch (change.getType()) {
+ case TYPE_TASK_FRAGMENT_APPEARED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ if (!mTaskIdToFragmentTokens.contains(taskId)) {
+ mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
+ }
+ mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
+ onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
+ mTaskIdToConfigurations.get(taskId));
+
+ onTaskFragmentAppeared(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_INFO_CHANGED:
+ onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_VANISHED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ if (mTaskIdToFragmentTokens.contains(taskId)) {
+ final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+ tokens.remove(change.getTaskFragmentToken());
+ if (tokens.isEmpty()) {
+ mTaskIdToFragmentTokens.remove(taskId);
+ mTaskIdToConfigurations.remove(taskId);
+ }
+ }
+
+ onTaskFragmentVanished(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
+
+ onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
+ break;
+ case TYPE_TASK_FRAGMENT_ERROR:
+ final Bundle errorBundle = change.getErrorBundle();
+ onTaskFragmentError(
+ change.getErrorCallbackToken(),
+ errorBundle.getParcelable(
+ KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
+ errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+ errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+ java.lang.Throwable.class));
+ break;
+ case TYPE_ACTIVITY_REPARENT_TO_TASK:
+ onActivityReparentToTask(
+ change.getTaskId(),
+ change.getActivityIntent(),
+ change.getActivityToken());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown TaskFragmentEvent=" + change.getType());
+ }
+ }
+ }
+
@Override
public void applyTransaction(@NonNull WindowContainerTransaction t) {
t.setTaskFragmentOrganizer(mInterface);
@@ -203,51 +310,8 @@
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentParentInfoChanged(
- @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
- fragmentToken, parentConfig));
- }
-
- @Override
- public void onTaskFragmentError(
- @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
- mExecutor.execute(() -> {
- final TaskFragmentInfo info = errorBundle.getParcelable(
- KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
- TaskFragmentOrganizer.this.onTaskFragmentError(
- errorCallbackToken, info,
- errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
- (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
- java.lang.Throwable.class));
- });
- }
-
- @Override
- public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
- @NonNull IBinder activityToken) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onActivityReparentToTask(
- taskId, activityIntent, activityToken));
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
}
};
diff --git a/core/java/android/window/TaskFragmentTransaction.aidl b/core/java/android/window/TaskFragmentTransaction.aidl
new file mode 100644
index 0000000..aaa2db4
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+parcelable TaskFragmentTransaction;
+parcelable TaskFragmentTransaction.Change;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
new file mode 100644
index 0000000..755864f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to communicate information about what are changing on embedded TaskFragments belonging to
+ * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
+ * @see TaskFragmentTransaction.Change
+ * @hide
+ */
+public final class TaskFragmentTransaction implements Parcelable {
+
+ private final ArrayList<Change> mChanges = new ArrayList<>();
+
+ public TaskFragmentTransaction() {}
+
+ private TaskFragmentTransaction(Parcel in) {
+ in.readTypedList(mChanges, Change.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mChanges);
+ }
+
+ /** Adds a {@link Change} to this transaction. */
+ public void addChange(@Nullable Change change) {
+ if (change != null) {
+ mChanges.add(change);
+ }
+ }
+
+ /** Whether this transaction contains any {@link Change}. */
+ public boolean isEmpty() {
+ return mChanges.isEmpty();
+ }
+
+ @NonNull
+ public List<Change> getChanges() {
+ return mChanges;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("TaskFragmentTransaction{changes=[");
+ for (int i = 0; i < mChanges.size(); ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mChanges.get(i));
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
+ @Override
+ public TaskFragmentTransaction createFromParcel(Parcel in) {
+ return new TaskFragmentTransaction(in);
+ }
+
+ @Override
+ public TaskFragmentTransaction[] newArray(int size) {
+ return new TaskFragmentTransaction[size];
+ }
+ };
+
+ /** Change type: the TaskFragment is attached to the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
+
+ /** Change type: the status of the TaskFragment is changed. */
+ public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
+
+ /** Change type: the TaskFragment is removed form the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
+
+ /** Change type: the status of the parent leaf Task is changed. */
+ public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
+
+ /** Change type: the TaskFragment related operation failed on the server side. */
+ public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
+
+ /**
+ * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
+ * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
+ * we need to notify the organizer so that it can check if the Activity matches any split rule.
+ */
+ public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_TASK_FRAGMENT_APPEARED,
+ TYPE_TASK_FRAGMENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_VANISHED,
+ TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_ERROR,
+ TYPE_ACTIVITY_REPARENT_TO_TASK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeType {}
+
+ /** Represents the change an embedded TaskFragment undergoes. */
+ public static final class Change implements Parcelable {
+
+ /** @see ChangeType */
+ @ChangeType
+ private final int mType;
+
+ /** @see #setTaskFragmentToken(IBinder) */
+ @Nullable
+ private IBinder mTaskFragmentToken;
+
+ /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
+ @Nullable
+ private TaskFragmentInfo mTaskFragmentInfo;
+
+ /** @see #setTaskId(int) */
+ private int mTaskId;
+
+ /** @see #setTaskConfiguration(Configuration) */
+ @Nullable
+ private Configuration mTaskConfiguration;
+
+ /** @see #setErrorCallbackToken(IBinder) */
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
+ /** @see #setErrorBundle(Bundle) */
+ @Nullable
+ private Bundle mErrorBundle;
+
+ /** @see #setActivityIntent(Intent) */
+ @Nullable
+ private Intent mActivityIntent;
+
+ /** @see #setActivityToken(IBinder) */
+ @Nullable
+ private IBinder mActivityToken;
+
+ public Change(@ChangeType int type) {
+ mType = type;
+ }
+
+ private Change(Parcel in) {
+ mType = in.readInt();
+ mTaskFragmentToken = in.readStrongBinder();
+ mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+ mTaskId = in.readInt();
+ mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mErrorCallbackToken = in.readStrongBinder();
+ mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mActivityToken = in.readStrongBinder();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeStrongBinder(mTaskFragmentToken);
+ dest.writeTypedObject(mTaskFragmentInfo, flags);
+ dest.writeInt(mTaskId);
+ dest.writeTypedObject(mTaskConfiguration, flags);
+ dest.writeStrongBinder(mErrorCallbackToken);
+ dest.writeBundle(mErrorBundle);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeStrongBinder(mActivityToken);
+ }
+
+ /** The change is related to the TaskFragment created with this unique token. */
+ public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
+ mTaskFragmentToken = requireNonNull(taskFragmentToken);
+ return this;
+ }
+
+ /** Info of the embedded TaskFragment. */
+ public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
+ mTaskFragmentInfo = requireNonNull(info);
+ return this;
+ }
+
+ /** Task id the parent Task. */
+ public Change setTaskId(int taskId) {
+ mTaskId = taskId;
+ return this;
+ }
+
+ /** Configuration of the parent Task. */
+ public Change setTaskConfiguration(@NonNull Configuration configuration) {
+ mTaskConfiguration = requireNonNull(configuration);
+ return this;
+ }
+
+ /**
+ * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
+ * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
+ * report back.
+ */
+ public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
+ mErrorCallbackToken = errorCallbackToken;
+ return this;
+ }
+
+ /**
+ * Bundle with necessary info about the failure operation of
+ * {@link #TYPE_TASK_FRAGMENT_ERROR}.
+ */
+ public Change setErrorBundle(@NonNull Bundle errorBundle) {
+ mErrorBundle = requireNonNull(errorBundle);
+ return this;
+ }
+
+ /**
+ * Intent of the activity that is reparented to the Task for
+ * {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ */
+ public Change setActivityIntent(@NonNull Intent intent) {
+ mActivityIntent = requireNonNull(intent);
+ return this;
+ }
+
+ /**
+ * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ * If the activity belongs to the same process as the organizer, this will be the actual
+ * activity token; if the activity belongs to a different process, the server will generate
+ * a temporary token that the organizer can use to reparent the activity through
+ * {@link WindowContainerTransaction} if needed.
+ */
+ public Change setActivityToken(@NonNull IBinder activityToken) {
+ mActivityToken = requireNonNull(activityToken);
+ return this;
+ }
+
+ @ChangeType
+ public int getType() {
+ return mType;
+ }
+
+ @Nullable
+ public IBinder getTaskFragmentToken() {
+ return mTaskFragmentToken;
+ }
+
+ @Nullable
+ public TaskFragmentInfo getTaskFragmentInfo() {
+ return mTaskFragmentInfo;
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ @Nullable
+ public Configuration getTaskConfiguration() {
+ return mTaskConfiguration;
+ }
+
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
+ @Nullable
+ public Bundle getErrorBundle() {
+ return mErrorBundle;
+ }
+
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public IBinder getActivityToken() {
+ return mActivityToken;
+ }
+
+ @Override
+ public String toString() {
+ return "Change{ type=" + mType + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Change> CREATOR = new Creator<>() {
+ @Override
+ public Change createFromParcel(Parcel in) {
+ return new Change(in);
+ }
+
+ @Override
+ public Change[] newArray(int size) {
+ return new Change[size];
+ }
+ };
+ }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 1d396be..0730f3d 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -325,7 +325,8 @@
public boolean checkApplicationCallbackRegistration(int priority,
OnBackInvokedCallback callback) {
if (!mApplicationCallBackEnabled
- && !(callback instanceof CompatOnBackInvokedCallback)) {
+ && !(callback instanceof CompatOnBackInvokedCallback)
+ && !ALWAYS_ENFORCE_PREDICTIVE_BACK) {
Log.w("OnBackInvokedCallback",
"OnBackInvokedCallback is not enabled for the application."
+ "\nSet 'android:enableOnBackInvokedCallback=\"true\"' in the"
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dbfa4d3..48343803 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -66,6 +66,8 @@
private static final long INVALID_ID = -1;
public static final int NANOS_IN_MILLISECOND = 1_000_000;
+ private static final int MAX_LENGTH_EVENT_DESC = 20;
+
static final int REASON_END_UNKNOWN = -1;
static final int REASON_END_NORMAL = 0;
static final int REASON_END_SURFACE_DESTROYED = 1;
@@ -394,7 +396,18 @@
return true;
}
- private void markEvent(String desc) {
+ /**
+ * Mark the FrameTracker events in the trace.
+ *
+ * @param desc The description of the trace event,
+ * shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}.
+ */
+ private void markEvent(@NonNull String desc) {
+ if (desc.length() > MAX_LENGTH_EVENT_DESC) {
+ throw new IllegalArgumentException(TextUtils.formatSimple(
+ "The length of the trace event description <%s> exceeds %d",
+ desc, MAX_LENGTH_EVENT_DESC));
+ }
Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
Trace.endSection();
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e625b31..72de78c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -149,6 +149,10 @@
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
+ @VisibleForTesting
+ public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+ private static final int MAX_LENGTH_SESSION_NAME = 100;
+
public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
@@ -732,6 +736,9 @@
* @return the name of the cuj type
*/
public static String getNameOfCuj(int cujType) {
+ // Please note:
+ // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+ // 2. The returned string should be the same with the name defined in atoms.proto.
switch (cujType) {
case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
return "SHADE_EXPAND_COLLAPSE";
@@ -1106,9 +1113,27 @@
public Session(@CujType int cujType, @NonNull String postfix) {
mCujType = cujType;
mTimeStamp = System.nanoTime();
- mName = TextUtils.isEmpty(postfix)
- ? String.format("J<%s>", getNameOfCuj(mCujType))
- : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
+ mName = generateSessionName(getNameOfCuj(cujType), postfix);
+ }
+
+ private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
+ final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
+ // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+ if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
+ throw new IllegalArgumentException(TextUtils.formatSimple(
+ "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
+ }
+ if (hasPostfix) {
+ final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
+ if (cujPostfix.length() > remaining) {
+ cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
+ }
+ }
+ // The max length of the whole string should be:
+ // 105 with postfix, 83 without postfix
+ return hasPostfix
+ ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
+ : TextUtils.formatSimple("J<%s>", cujName);
}
@CujType
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 0a29fc52..fa6fa55 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1169,7 +1169,15 @@
}
+ /** @hide */
+ public static void startForWifi(Context context) {
+ new BinderCallsStats.SettingsObserver(
+ context,
+ new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.WIFI));
+ }
/**
* Settings observer for other processes (not system_server).
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8e54746..b9243ec 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -219,8 +219,9 @@
throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
}
- HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, android.hardware.HardwareBuffer.class);
- ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE, android.graphics.ParcelableColorSpace.class);
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+ ParcelableColorSpace.class);
return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
colorSpace.getColorSpace());
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 578b588..2c9ef4f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -339,6 +339,8 @@
// A map from package name of vendor APEXes that can be updated to an installer package name
// allowed to install updates for it.
private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+ // A set of package names that are allowed to use <install-constraints> manifest tag.
+ private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>();
private String mModulesInstallerPackageName;
@@ -535,6 +537,10 @@
return mAllowedVendorApexes;
}
+ public Set<String> getInstallConstraintsAllowlist() {
+ return mInstallConstraintsAllowlist;
+ }
+
public String getModulesInstallerPackageName() {
return mModulesInstallerPackageName;
}
@@ -1455,6 +1461,20 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "install-constraints-allowed": {
+ if (allowAppConfigs) {
+ String packageName = parser.getAttributeValue(null, "package");
+ if (packageName == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mInstallConstraintsAllowlist.add(packageName);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 22f84b6..9ae1630 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1116,12 +1116,6 @@
return array;
}
-static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) {
- PhysicalDisplayId displayId;
- SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId);
- return static_cast<jlong>(displayId.value);
-}
-
static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
if (!id) return nullptr;
@@ -2210,8 +2204,6 @@
(void*)nativeSetDefaultFrameRateCompatibility},
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
- {"nativeGetPrimaryPhysicalDisplayId", "()J",
- (void*)nativeGetPrimaryPhysicalDisplayId },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
(void*)nativeGetPhysicalDisplayToken },
{"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d686dd2..34b6a54 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,7 +51,5 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
-
- <integer name="config_chooser_max_targets_per_row">6</integer>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0acf703..062523e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3607,4 +3607,19 @@
false, the application cannot be profiled at all. Defaults to true. -->
<attr name="enabled" format="boolean" />
</declare-styleable>
+
+ <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by
+ its child elements is true.
+ It is possible to have multiple <code>install-constraints</code> tags in a single manifest,
+ where each tag is evaluated independently.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" />
+
+ <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint
+ starts with the given prefix.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix"
+ parent="AndroidManifestInstallConstraints">
+ <attr name="value" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9d2c3d7..d176a1d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2629,6 +2629,10 @@
will be locked. -->
<bool name="config_multiuserDelayUserDataLocking">false</bool>
+ <!-- Whether the device allows users to start in background on secondary displays.
+ Should be false for most devices, except automotive vehicle with passenger displays. -->
+ <bool name="config_multiuserUsersOnSecondaryDisplays">false</bool>
+
<!-- Whether to automatically switch a non-primary user back to the primary user after a
timeout when the device is docked. -->
<bool name="config_enableTimeoutToUserZeroWhenDocked">false</bool>
@@ -5850,4 +5854,7 @@
<!-- Whether the wake screen on notifications feature is available. -->
<bool name="config_pulseOnNotificationsAvailable">true</bool>
+
+ <!-- The number of tasks to scan to get the visibility of Home -->
+ <integer name="config_maxScanTasksForHomeVisibility">10</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cb0393f..e6ae7b1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -453,6 +453,7 @@
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
+ <java-symbol type="bool" name="config_multiuserUsersOnSecondaryDisplays" />
<java-symbol type="bool" name="config_enableTimeoutToUserZeroWhenDocked" />
<java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
<java-symbol type="xml" name="config_user_types" />
@@ -3977,6 +3978,8 @@
<java-symbol type="string" name="config_customCountryDetector" />
+ <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
+
<!-- For Foldables -->
<java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 669138c..0d5cd72 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -157,6 +157,22 @@
assertFalse(config.isNightModeActive());
}
+ @Test
+ public void testSequenceNumberWrapAround() {
+ Configuration config = new Configuration();
+ Configuration otherConfig = new Configuration();
+
+ // Verify the other configuration is not newer when this sequence number warp around.
+ config.seq = 1000;
+ otherConfig.seq = Integer.MAX_VALUE - 1000;
+ assertFalse(config.isOtherSeqNewer(otherConfig));
+
+ // Verify the other configuration is newer when the other sequence number warp around.
+ config.seq = Integer.MAX_VALUE - 1000;
+ otherConfig.seq = 1000;
+ assertTrue(config.isOtherSeqNewer(otherConfig));
+ }
+
private void dumpDebug(File f, Configuration config) throws Exception {
final AtomicFile af = new AtomicFile(f);
FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
deleted file mode 100644
index 6cc42f6..0000000
--- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Bitmap.Config;
-import android.graphics.Path.Direction;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class PathOffsetTest {
-
- private static final int SQUARE = 10;
- private static final int WIDTH = 100;
- private static final int HEIGHT = 100;
- private static final int START_X = 10;
- private static final int START_Y = 20;
- private static final int OFFSET_X = 30;
- private static final int OFFSET_Y = 40;
-
- @Test
- @SmallTest
- public void testPathOffset() {
- Path actualPath = new Path();
- actualPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
- assertTrue(actualPath.isSimplePath);
- actualPath.offset(OFFSET_X, OFFSET_Y);
- assertTrue(actualPath.isSimplePath);
-
- Path expectedPath = new Path();
- expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
- START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
- assertPaths(actualPath, expectedPath);
- }
-
- @Test
- @SmallTest
- public void testPathOffsetWithDestination() {
- Path initialPath = new Path();
- initialPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
- Path actualPath = new Path();
- assertTrue(initialPath.isSimplePath);
- assertTrue(actualPath.isSimplePath);
- initialPath.offset(OFFSET_X, OFFSET_Y, actualPath);
- assertTrue(actualPath.isSimplePath);
-
- Path expectedPath = new Path();
- expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
- START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
- assertPaths(actualPath, expectedPath);
- }
-
- private static void assertPaths(Path actual, Path expected) {
- Bitmap actualBitmap = drawAndGetBitmap(actual);
- Bitmap expectedBitmap = drawAndGetBitmap(expected);
- assertTrue(actualBitmap.sameAs(expectedBitmap));
- }
-
- private static Bitmap drawAndGetBitmap(Path path) {
- Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Config.ARGB_8888);
- bitmap.eraseColor(Color.BLACK);
- Paint paint = new Paint();
- paint.setColor(Color.RED);
- Canvas canvas = new Canvas(bitmap);
- canvas.drawPath(path, paint);
- return bitmap;
- }
-
-}
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
deleted file mode 100644
index b50792c..0000000
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-public class PathTest extends TestCase {
-
- @SmallTest
- public void testResetPreservesFillType() throws Exception {
- Path path = new Path();
-
- final Path.FillType defaultFillType = path.getFillType();
- final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
-
- // This test is only meaningful if it changes from the default.
- assertFalse(fillType.equals(defaultFillType));
-
- path.setFillType(fillType);
- path.reset();
- assertEquals(path.getFillType(), fillType);
- }
-}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index c1e72fe..32c3a26 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -58,10 +58,10 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import libcore.io.Streams;
-
import com.google.android.collect.Sets;
+import libcore.io.Streams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -477,6 +477,14 @@
new File(mTarget, "test (1).jpg").createNewFile();
assertNameEquals("test (2).jpg",
FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+
+ assertNameEquals("test.mp3", FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test.mp3").createNewFile();
+ assertNameEquals("test (1).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test (1).mp3").createNewFile();
+ assertNameEquals("test (2).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index d96f041..1519e48 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,10 +16,26 @@
package com.android.internal.jank;
+import static android.text.TextUtils.formatSimple;
+
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_LAUNCH_CAMERA;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
+import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
+import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,6 +54,7 @@
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.DeviceConfig;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -52,8 +69,12 @@
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
+import com.android.internal.util.FrameworkStatsLog;
+
+import com.google.common.truth.Expect;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -64,11 +85,17 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
@SmallTest
public class InteractionJankMonitorTest {
private static final String CUJ_POSTFIX = "";
+ private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
+ private static final SparseArray<String> CUJ_NAME_EXCEPTION_MAP = new SparseArray<>();
+ private static final String ENUM_NAME_PREFIX =
+ "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
+
private ViewAttachTestActivity mActivity;
private View mView;
private HandlerThread mWorker;
@@ -77,6 +104,53 @@
public ActivityTestRule<ViewAttachTestActivity> mRule =
new ActivityTestRule<>(ViewAttachTestActivity.class);
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @BeforeClass
+ public static void initialize() {
+ ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
+ ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
+
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, "CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+ "CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, "CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND, "CUJ_NOTIFICATION_SHADE_ROW_EXPAND");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE, "CUJ_NOTIFICATION_SHADE_ROW_SWIPE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING, "CUJ_NOTIFICATION_SHADE_SCROLL_FLING");
+ CUJ_NAME_EXCEPTION_MAP.put(CUJ_LOCKSCREEN_LAUNCH_CAMERA, "CUJ_LOCKSCREEN_LAUNCH_CAMERA");
+ CUJ_NAME_EXCEPTION_MAP.put(CUJ_SPLIT_SCREEN_RESIZE, "CUJ_SPLIT_SCREEN_RESIZE");
+ }
+
+ private static String getEnumName(String name) {
+ return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
+ }
+
@Before
public void setup() {
// Prepare an activity for getting ThreadedRenderer later.
@@ -174,6 +248,82 @@
}
}
+ @Test
+ public void testCujsMapToEnumsCorrectly() {
+ List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("CUJ_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toList());
+
+ Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+ cujs.forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ final String cujName = f.getName();
+ final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
+ ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
+ : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
+ final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
+ final String enumName = enumsMap.get(enumKey);
+ final String expectedNameOfCuj = CUJ_NAME_EXCEPTION_MAP.contains(cuj)
+ ? CUJ_NAME_EXCEPTION_MAP.get(cuj)
+ : formatSimple("CUJ_%s", getNameOfCuj(cuj));
+ mExpect
+ .withMessage(formatSimple(
+ "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
+ .that(expectedEnumName.equals(enumName))
+ .isTrue();
+ mExpect
+ .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+ .that(cujName.equals(expectedNameOfCuj))
+ .isTrue();
+ });
+ }
+
+ @Test
+ public void testCujNameLimit() {
+ Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("CUJ_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ mExpect
+ .withMessage(formatSimple(
+ "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
+ .that(getNameOfCuj(cuj).length())
+ .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
+ });
+ }
+
+ @Test
+ public void testSessionNameLengthLimit() {
+ final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+ final String cujName = getNameOfCuj(cujType);
+ final String cujTag = "ThisIsTheCujTag";
+ final String tooLongTag = cujTag.repeat(10);
+
+ // Normal case, no postfix.
+ Session noPostfix = new Session(cujType, "");
+ assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));
+
+ // Normal case, with postfix.
+ Session withPostfix = new Session(cujType, cujTag);
+ assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
+
+ // Since the length of the cuj name is tested in another test, no need to test it here.
+ // Too long postfix case, should trim the postfix and keep the cuj name completed.
+ final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
+ "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagT...");
+ Session longPostfix = new Session(cujType, tooLongTag);
+ assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
+ }
+
private InteractionJankMonitor createMockedInteractionJankMonitor() {
InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
doReturn(true).when(monitor).shouldMonitor(anyInt());
@@ -217,4 +367,12 @@
return tracker;
}
+
+ private int getIntFieldChecked(Field field) {
+ try {
+ return field.getInt(null);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
diff --git a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
index 7c76498..c88ab90 100644
--- a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
+++ b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -34,7 +35,6 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.quality.Strictness;
@@ -56,16 +56,11 @@
private Parcel mParcelSpy;
private Bundle mBundle;
- @Before
- public void setUp() throws Exception {
- setUpBundle(/* hasLazy */ true);
- }
-
@Test
public void bundleClear_whenUnparcelledWithoutLazy_recyclesParcelOnce() {
- setUpBundle(/* hasLazy */ false);
+ setUpBundle(/* lazy */ 0, /* nonLazy */ 1);
// Will unparcel and immediately recycle parcel
- assertNotNull(mBundle.getString("key"));
+ assertNotNull(mBundle.getString("nonLazy0"));
verify(mParcelSpy, times(1)).recycle();
assertFalse(mBundle.isDefinitelyEmpty());
@@ -77,6 +72,7 @@
@Test
public void bundleClear_whenParcelled_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
assertTrue(mBundle.isParcelled());
verify(mParcelSpy, times(0)).recycle();
@@ -90,23 +86,9 @@
}
@Test
- public void bundleClear_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
- // Will unparcel with a lazy value, and immediately unwrap the lazy value,
- // with no lazy values left at the end of getParcelable
- assertNotNull(mBundle.getParcelable("key", CustomParcelable.class));
- verify(mParcelSpy, times(0)).recycle();
-
- mBundle.clear();
- verify(mParcelSpy, times(1)).recycle();
- assertTrue(mBundle.isDefinitelyEmpty());
-
- // Should not recycle again
- mBundle.clear();
- verify(mParcelSpy, times(1)).recycle();
- }
-
- @Test
public void bundleClear_whenUnparcelledWithLazy_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
+
// Will unparcel but keep the CustomParcelable lazy
assertFalse(mBundle.isEmpty());
verify(mParcelSpy, times(0)).recycle();
@@ -122,6 +104,8 @@
@Test
public void bundleClear_whenClearedWithSharedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
Bundle copy = new Bundle();
copy.putAll(mBundle);
@@ -136,6 +120,8 @@
@Test
public void bundleClear_whenClearedWithCopiedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
// Will unparcel but keep the CustomParcelable lazy
assertFalse(mBundle.isEmpty());
@@ -151,7 +137,101 @@
verify(mParcelSpy, never()).recycle();
}
- private void setUpBundle(boolean hasLazy) {
+ @Test
+ public void bundleGet_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
+
+ // Will unparcel with a lazy value, and immediately unwrap the lazy value,
+ // with no lazy values left at the end of getParcelable
+ // Ref counting should immediately recycle
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_whenMultipleLazy_recyclesParcelWhenAllUnwrapped() {
+ setUpBundle(/* lazy */ 2);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy1", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ assertTrue(mBundle.isDefinitelyEmpty());
+ }
+
+ @Test
+ public void bundleGet_whenLazyAndNonLazy_recyclesParcelWhenAllUnwrapped() {
+ setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getString("nonLazy0"));
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_whenLazyAndNonLazy_doesNotRecycleWhenOnlyNonLazyRetrieved() {
+ setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+ assertNotNull(mBundle.getString("nonLazy0"));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getString("nonLazy0"));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_withWithSharedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
+ Bundle copy = new Bundle();
+ copy.putAll(mBundle);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+
+ assertNotNull(copy.getParcelable("lazy0", CustomParcelable.class));
+ copy.clear();
+
+ verify(mParcelSpy, never()).recycle();
+ }
+
+ @Test
+ public void bundleGet_getAfterLazyCleared_doesNotRecycleAgain() {
+ setUpBundle(/* lazy */ 1);
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+
+ assertNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ private void setUpBundle(int lazy) {
+ setUpBundle(lazy, /* nonLazy */ 0);
+ }
+
+ private void setUpBundle(int lazy, int nonLazy) {
AtomicReference<Parcel> parcel = new AtomicReference<>();
StaticMockitoSession session = mockitoSession()
.strictness(Strictness.STRICT_STUBS)
@@ -166,7 +246,7 @@
Bundle bundle = new Bundle();
bundle.setClassLoader(getClass().getClassLoader());
- Parcel p = createBundle(hasLazy);
+ Parcel p = createBundle(lazy, nonLazy);
bundle.readFromParcel(p);
p.recycle();
@@ -179,13 +259,17 @@
/**
* Create a test bundle, parcel it and return the parcel.
*/
- private Parcel createBundle(boolean hasLazy) {
+ private Parcel createBundle(int lazy, int nonLazy) {
final Bundle source = new Bundle();
- if (hasLazy) {
- source.putParcelable("key", new CustomParcelable(13, "Tiramisu"));
- } else {
- source.putString("key", "tiramisu");
+
+ for (int i = 0; i < nonLazy; i++) {
+ source.putString("nonLazy" + i, "Tiramisu");
}
+
+ for (int i = 0; i < lazy; i++) {
+ source.putParcelable("lazy" + i, new CustomParcelable(13, "Tiramisu"));
+ }
+
return getParcelledBundle(source);
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 425a378..a8ab6d9 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -330,11 +330,7 @@
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
throwIfHasHwFeaturesInSwMode(paint);
- if (path.isSimplePath && path.rects != null) {
- nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
- } else {
- nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
- }
+ nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
public void drawPoint(float x, float y, @NonNull Paint paint) {
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index a998ba8..d06f665 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -286,11 +286,7 @@
@Override
public final void drawPath(@NonNull Path path, @NonNull Paint paint) {
- if (path.isSimplePath && path.rects != null) {
- nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
- } else {
- nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
- }
+ nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
@Override
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7cc22d7..0e67f1f 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1046,14 +1046,39 @@
}
/** @hide */
- public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
- if (srcRect == null) {
- // Empty rect means entire surface
- return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap.getNativeInstance());
- } else {
- return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
- srcRect.right, srcRect.bottom, bitmap.getNativeInstance());
+ public abstract static class CopyRequest {
+ protected Bitmap mDestinationBitmap;
+ final Rect mSrcRect;
+
+ protected CopyRequest(Rect srcRect, Bitmap destinationBitmap) {
+ mDestinationBitmap = destinationBitmap;
+ if (srcRect != null) {
+ mSrcRect = srcRect;
+ } else {
+ mSrcRect = new Rect();
+ }
}
+
+ /**
+ * Retrieve the bitmap in which to store the result of the copy request
+ */
+ public long getDestinationBitmap(int srcWidth, int srcHeight) {
+ if (mDestinationBitmap == null) {
+ mDestinationBitmap =
+ Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+ }
+ return mDestinationBitmap.getNativeInstance();
+ }
+
+ /** Called when the copy is completed */
+ public abstract void onCopyFinished(int result);
+ }
+
+ /** @hide */
+ public static void copySurfaceInto(Surface surface, CopyRequest copyRequest) {
+ final Rect srcRect = copyRequest.mSrcRect;
+ nCopySurfaceInto(surface, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
+ copyRequest);
}
/**
@@ -1464,8 +1489,8 @@
private static native void nRemoveObserver(long nativeProxy, long nativeObserver);
- private static native int nCopySurfaceInto(Surface surface,
- int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
+ private static native void nCopySurfaceInto(Surface surface,
+ int srcLeft, int srcTop, int srcRight, int srcBottom, CopyRequest request);
private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index e5ef10d..afcaabe 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,8 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -47,18 +45,6 @@
public final long mNativePath;
/**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean isSimplePath = true;
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Region rects;
- private Direction mLastDirection = null;
-
- /**
* Create an empty path
*/
public Path() {
@@ -72,15 +58,7 @@
* @param src The path to copy from when initializing the new path
*/
public Path(@Nullable Path src) {
- long valNative = 0;
- if (src != null) {
- valNative = src.mNativePath;
- isSimplePath = src.isSimplePath;
- if (src.rects != null) {
- rects = new Region(src.rects);
- }
- }
- mNativePath = nInit(valNative);
+ mNativePath = nInit(src != null ? src.mNativePath : 0);
sRegistry.registerNativeAllocation(this, mNativePath);
}
@@ -89,9 +67,6 @@
* This does NOT change the fill-type setting.
*/
public void reset() {
- isSimplePath = true;
- mLastDirection = null;
- if (rects != null) rects.setEmpty();
// We promised not to change this, so preserve it around the native
// call, which does now reset fill type.
final FillType fillType = getFillType();
@@ -104,9 +79,6 @@
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
- isSimplePath = true;
- mLastDirection = null;
- if (rects != null) rects.setEmpty();
nRewind(mNativePath);
}
@@ -116,19 +88,7 @@
if (this == src) {
return;
}
- isSimplePath = src.isSimplePath;
nSet(mNativePath, src.mNativePath);
- if (!isSimplePath) {
- return;
- }
-
- if (rects != null && src.rects != null) {
- rects.set(src.rects);
- } else if (rects != null && src.rects == null) {
- rects.setEmpty();
- } else if (src.rects != null) {
- rects = new Region(src.rects);
- }
}
/**
@@ -192,12 +152,7 @@
* @see #op(Path, android.graphics.Path.Op)
*/
public boolean op(@NonNull Path path1, @NonNull Path path2, @NonNull Op op) {
- if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
- isSimplePath = false;
- rects = null;
- return true;
- }
- return false;
+ return nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath);
}
/**
@@ -378,7 +333,6 @@
* @param y The y-coordinate of the end of a line
*/
public void lineTo(float x, float y) {
- isSimplePath = false;
nLineTo(mNativePath, x, y);
}
@@ -393,7 +347,6 @@
* this contour, to specify a line
*/
public void rLineTo(float dx, float dy) {
- isSimplePath = false;
nRLineTo(mNativePath, dx, dy);
}
@@ -408,7 +361,6 @@
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
- isSimplePath = false;
nQuadTo(mNativePath, x1, y1, x2, y2);
}
@@ -427,7 +379,6 @@
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
- isSimplePath = false;
nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
@@ -445,7 +396,6 @@
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
- isSimplePath = false;
nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -456,7 +406,6 @@
*/
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
- isSimplePath = false;
nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -507,7 +456,6 @@
*/
public void arcTo(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean forceMoveTo) {
- isSimplePath = false;
nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
@@ -516,7 +464,6 @@
* first point of the contour, a line segment is automatically added.
*/
public void close() {
- isSimplePath = false;
nClose(mNativePath);
}
@@ -536,18 +483,6 @@
final int nativeInt;
}
- private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
- if (mLastDirection == null) {
- mLastDirection = dir;
- }
- if (mLastDirection != dir) {
- isSimplePath = false;
- } else {
- if (rects == null) rects = new Region();
- rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
- }
- }
-
/**
* Add a closed rectangle contour to the path
*
@@ -568,7 +503,6 @@
* @param dir The direction to wind the rectangle's contour
*/
public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) {
- detectSimplePath(left, top, right, bottom, dir);
nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -588,7 +522,6 @@
* @param dir The direction to wind the oval's contour
*/
public void addOval(float left, float top, float right, float bottom, @NonNull Direction dir) {
- isSimplePath = false;
nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -601,7 +534,6 @@
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, @NonNull Direction dir) {
- isSimplePath = false;
nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
}
@@ -624,7 +556,6 @@
*/
public void addArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle) {
- isSimplePath = false;
nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
@@ -649,7 +580,6 @@
*/
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Direction dir) {
- isSimplePath = false;
nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
}
@@ -682,7 +612,6 @@
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
- isSimplePath = false;
nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
}
@@ -693,7 +622,6 @@
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(@NonNull Path src, float dx, float dy) {
- isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, dx, dy);
}
@@ -703,7 +631,6 @@
* @param src The path that is appended to the current path
*/
public void addPath(@NonNull Path src) {
- isSimplePath = false;
nAddPath(mNativePath, src.mNativePath);
}
@@ -713,7 +640,6 @@
* @param src The path to add as a new contour
*/
public void addPath(@NonNull Path src, @NonNull Matrix matrix) {
- if (!src.isSimplePath) isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, matrix.ni());
}
@@ -741,15 +667,6 @@
* @param dy The amount in the Y direction to offset the entire path
*/
public void offset(float dx, float dy) {
- if (isSimplePath && rects == null) {
- // nothing to offset
- return;
- }
- if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) {
- rects.translate((int) dx, (int) dy);
- } else {
- isSimplePath = false;
- }
nOffset(mNativePath, dx, dy);
}
@@ -760,7 +677,6 @@
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
- isSimplePath = false;
nSetLastPoint(mNativePath, dx, dy);
}
@@ -773,12 +689,7 @@
* then the the original path is modified
*/
public void transform(@NonNull Matrix matrix, @Nullable Path dst) {
- long dstNative = 0;
- if (dst != null) {
- dst.isSimplePath = false;
- dstNative = dst.mNativePath;
- }
- nTransform(mNativePath, matrix.ni(), dstNative);
+ nTransform(mNativePath, matrix.ni(), dst != null ? dst.mNativePath : 0);
}
/**
@@ -787,7 +698,6 @@
* @param matrix The matrix to apply to the path
*/
public void transform(@NonNull Matrix matrix) {
- isSimplePath = false;
nTransform(mNativePath, matrix.ni());
}
@@ -797,7 +707,6 @@
}
final long mutateNI() {
- isSimplePath = false;
return mNativePath;
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 8ae0d3d..25e1922 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1557,16 +1557,6 @@
return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
}
- /** @hide */
- public List<FontFamily> getFallback() {
- ArrayList<FontFamily> families = new ArrayList<>();
- int familySize = nativeGetFamilySize(native_instance);
- for (int i = 0; i < familySize; ++i) {
- families.add(new FontFamily(nativeGetFamily(native_instance, i)));
- }
- return families;
- }
-
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
@@ -1592,13 +1582,6 @@
@CriticalNative
private static native long nativeGetReleaseFunc();
- @CriticalNative
- private static native int nativeGetFamilySize(long naitvePtr);
-
- @CriticalNative
- private static native long nativeGetFamily(long nativePtr, int index);
-
-
private static native void nativeRegisterGenericFamily(String str, long nativePtr);
private static native int nativeWriteTypefaces(
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 6939a72..47de37d 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -359,10 +359,10 @@
/** @hide */
@TestApi
public Region getSafeZone() {
- mMaskMatrix.reset();
+ Path mask = getIconMask();
mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
Path p = new Path();
- mMask.transform(mMaskMatrix, p);
+ mask.transform(mMaskMatrix, p);
Region safezoneRegion = new Region(getBounds());
safezoneRegion.setPath(p, safezoneRegion);
return safezoneRegion;
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 2797a4d..0f82c8f 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -20,12 +20,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
import android.graphics.Rect;
import android.os.Handler;
import android.view.ViewTreeObserver.OnDrawListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Provides a mechanisms to issue pixel copy requests to allow for copy
@@ -183,12 +186,10 @@
if (srcRect != null && srcRect.isEmpty()) {
throw new IllegalArgumentException("sourceRect is empty");
}
- // TODO: Make this actually async and fast and cool and stuff
- int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest);
- listenerThread.post(new Runnable() {
+ HardwareRenderer.copySurfaceInto(source, new HardwareRenderer.CopyRequest(srcRect, dest) {
@Override
- public void run() {
- listener.onPixelCopyFinished(result);
+ public void onCopyFinished(int result) {
+ listenerThread.post(() -> listener.onPixelCopyFinished(result));
}
});
}
@@ -255,30 +256,10 @@
@NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
@NonNull Handler listenerThread) {
validateBitmapDest(dest);
- if (source == null) {
- throw new IllegalArgumentException("source is null");
- }
- if (source.peekDecorView() == null) {
- throw new IllegalArgumentException(
- "Only able to copy windows with decor views");
- }
- Surface surface = null;
- final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
- if (root != null) {
- surface = root.mSurface;
- final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
- if (srcRect == null) {
- srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
- root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
- } else {
- srcRect.offset(surfaceInsets.left, surfaceInsets.top);
- }
- }
- if (surface == null || !surface.isValid()) {
- throw new IllegalArgumentException(
- "Window doesn't have a backing surface!");
- }
- request(surface, srcRect, dest, listener, listenerThread);
+ final Rect insets = new Rect();
+ final Surface surface = sourceForWindow(source, insets);
+ request(surface, adjustSourceRectForInsets(insets, srcRect), dest, listener,
+ listenerThread);
}
private static void validateBitmapDest(Bitmap bitmap) {
@@ -294,5 +275,235 @@
}
}
+ private static Surface sourceForWindow(Window source, Rect outInsets) {
+ if (source == null) {
+ throw new IllegalArgumentException("source is null");
+ }
+ if (source.peekDecorView() == null) {
+ throw new IllegalArgumentException(
+ "Only able to copy windows with decor views");
+ }
+ Surface surface = null;
+ final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
+ if (root != null) {
+ surface = root.mSurface;
+ final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
+ outInsets.set(surfaceInsets.left, surfaceInsets.top,
+ root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
+ }
+ if (surface == null || !surface.isValid()) {
+ throw new IllegalArgumentException(
+ "Window doesn't have a backing surface!");
+ }
+ return surface;
+ }
+
+ private static Rect adjustSourceRectForInsets(Rect insets, Rect srcRect) {
+ if (srcRect == null) {
+ return insets;
+ }
+ if (insets != null) {
+ srcRect.offset(insets.left, insets.top);
+ }
+ return srcRect;
+ }
+
+ /**
+ * Contains the result of a PixelCopy request
+ */
+ public static final class CopyResult {
+ private int mStatus;
+ private Bitmap mBitmap;
+
+ private CopyResult(@CopyResultStatus int status, Bitmap bitmap) {
+ mStatus = status;
+ mBitmap = bitmap;
+ }
+
+ /**
+ * Returns the {@link CopyResultStatus} of the copy request.
+ */
+ public @CopyResultStatus int getStatus() {
+ return mStatus;
+ }
+
+ private void validateStatus() {
+ if (mStatus != SUCCESS) {
+ throw new IllegalStateException("Copy request didn't succeed, status = " + mStatus);
+ }
+ }
+
+ /**
+ * If the PixelCopy {@link Request} was given a destination bitmap with
+ * {@link Request#setDestinationBitmap(Bitmap)} then the returned bitmap will be the same
+ * as the one given. If no destination bitmap was provided, then this
+ * will contain the automatically allocated Bitmap to hold the result.
+ *
+ * @return the Bitmap the copy request was stored in.
+ * @throws IllegalStateException if {@link #getStatus()} is not SUCCESS
+ */
+ public @NonNull Bitmap getBitmap() {
+ validateStatus();
+ return mBitmap;
+ }
+ }
+
+ /**
+ * A builder to create the complete PixelCopy request, which is then executed by calling
+ * {@link #request()}
+ */
+ public static final class Request {
+ private Request(Surface source, Rect sourceInsets, Executor listenerThread,
+ Consumer<CopyResult> listener) {
+ this.mSource = source;
+ this.mSourceInsets = sourceInsets;
+ this.mListenerThread = listenerThread;
+ this.mListener = listener;
+ }
+
+ private final Surface mSource;
+ private final Consumer<CopyResult> mListener;
+ private final Executor mListenerThread;
+ private final Rect mSourceInsets;
+ private Rect mSrcRect;
+ private Bitmap mDest;
+
+ /**
+ * Sets the region of the source to copy from. By default, the entire source is copied to
+ * the output. If only a subset of the source is necessary to be copied, specifying a
+ * srcRect will improve performance by reducing
+ * the amount of data being copied.
+ *
+ * @param srcRect The area of the source to read from. Null or empty will be treated to
+ * mean the entire source
+ * @return this
+ */
+ public @NonNull Request setSourceRect(@Nullable Rect srcRect) {
+ this.mSrcRect = srcRect;
+ return this;
+ }
+
+ /**
+ * Specifies the output bitmap in which to store the result. By default, a Bitmap of format
+ * {@link android.graphics.Bitmap.Config#ARGB_8888} with a width & height matching that
+ * of the {@link #setSourceRect(Rect) source area} will be created to place the result.
+ *
+ * @param destination The bitmap to store the result, or null to have a bitmap
+ * automatically created of the appropriate size. If not null, must not
+ * be {@link Bitmap#isRecycled() recycled} and must be
+ * {@link Bitmap#isMutable() mutable}.
+ * @return this
+ */
+ public @NonNull Request setDestinationBitmap(@Nullable Bitmap destination) {
+ if (destination != null) {
+ validateBitmapDest(destination);
+ }
+ this.mDest = destination;
+ return this;
+ }
+
+ /**
+ * Executes the request.
+ */
+ public void request() {
+ if (!mSource.isValid()) {
+ mListenerThread.execute(() -> mListener.accept(
+ new CopyResult(ERROR_SOURCE_INVALID, null)));
+ return;
+ }
+ HardwareRenderer.copySurfaceInto(mSource, new HardwareRenderer.CopyRequest(
+ adjustSourceRectForInsets(mSourceInsets, mSrcRect), mDest) {
+ @Override
+ public void onCopyFinished(int result) {
+ mListenerThread.execute(() -> mListener.accept(
+ new CopyResult(result, mDestinationBitmap)));
+ }
+ });
+ }
+ }
+
+ /**
+ * Creates a PixelCopy request for the given {@link Window}
+ * @param source The Window to copy from
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofWindow(@NonNull Window source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ final Rect insets = new Rect();
+ final Surface surface = sourceForWindow(source, insets);
+ return new Request(surface, insets, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+ * attached to.
+ *
+ * Note that this copy request is not cropped to the area the View occupies by default. If that
+ * behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
+ * {@link Request#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
+ *
+ * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
+ * will be used to retrieve the window to copy from.
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofWindow(@NonNull View source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ if (source == null || !source.isAttachedToWindow()) {
+ throw new IllegalArgumentException(
+ "View must not be null & must be attached to window");
+ }
+ final Rect insets = new Rect();
+ Surface surface = null;
+ final ViewRootImpl root = source.getViewRootImpl();
+ if (root != null) {
+ surface = root.mSurface;
+ insets.set(root.mWindowAttributes.surfaceInsets);
+ }
+ if (surface == null || !surface.isValid()) {
+ throw new IllegalArgumentException(
+ "Window doesn't have a backing surface!");
+ }
+ return new Request(surface, insets, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the given {@link Surface}
+ *
+ * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofSurface(@NonNull Surface source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ if (source == null || !source.isValid()) {
+ throw new IllegalArgumentException("Source must not be null & must be valid");
+ }
+ return new Request(source, null, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the {@link Surface} belonging to the
+ * given {@link SurfaceView}
+ *
+ * @param source The SurfaceView to copy from. The backing surface must be
+ * {@link Surface#isValid() valid}
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofSurface(@NonNull SurfaceView source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
+ }
+
private PixelCopy() {}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 0dba6b0..d42fca2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -51,12 +51,6 @@
@VisibleForTesting
final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
- /**
- * Mapping from the client assigned unique token to the TaskFragment parent
- * {@link Configuration}.
- */
- final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
-
private final TaskFragmentCallback mCallback;
@VisibleForTesting
TaskFragmentAnimationController mAnimationController;
@@ -68,8 +62,7 @@
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
- @NonNull Configuration parentConfig);
+ void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken);
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
@@ -300,7 +293,6 @@
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
- mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
mCallback.onTaskFragmentVanished(taskFragmentInfo);
@@ -308,12 +300,9 @@
}
@Override
- public void onTaskFragmentParentInfoChanged(
- @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
- mFragmentParentConfigs.put(fragmentToken, parentConfig);
-
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
if (mCallback != null) {
- mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
+ mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
}
}
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 c688080..dad0739 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -155,6 +155,9 @@
container.setInfo(taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+ } else {
+ // Update with the latest Task configuration.
+ mPresenter.updateContainer(container);
}
updateCallbackIfNecessary();
}
@@ -233,19 +236,30 @@
}
@Override
- public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
- @NonNull Configuration parentConfig) {
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(fragmentToken);
- if (container != null) {
- onTaskConfigurationChanged(container.getTaskId(), parentConfig);
- if (isInPictureInPicture(parentConfig)) {
- // No need to update presentation in PIP until the Task exit PIP.
- return;
- }
- mPresenter.updateContainer(container);
- updateCallbackIfNecessary();
+ onTaskConfigurationChanged(taskId, parentConfig);
+ if (isInPictureInPicture(parentConfig)) {
+ // No need to update presentation in PIP until the Task exit PIP.
+ return;
}
+ final TaskContainer taskContainer = getTaskContainer(taskId);
+ if (taskContainer == null || taskContainer.isEmpty()) {
+ Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
+ return;
+ }
+ // Update all TaskFragments in the Task. Make a copy of the list since some may be
+ // removed on updating.
+ final List<TaskFragmentContainer> containers =
+ new ArrayList<>(taskContainer.mContainers);
+ for (int i = containers.size() - 1; i >= 0; i--) {
+ final TaskFragmentContainer container = containers.get(i);
+ // Wait until onTaskFragmentAppeared to update new container.
+ if (!container.isFinished() && !container.isWaitingActivityAppear()) {
+ mPresenter.updateContainer(container);
+ }
+ }
+ updateCallbackIfNecessary();
}
}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index f54ab08..918e514 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 5cba3b4..6ae0f9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -72,6 +73,7 @@
*/
public class ShellTaskOrganizer extends TaskOrganizer implements
CompatUIController.CompatUICallback {
+ private static final String TAG = "ShellTaskOrganizer";
// Intentionally using negative numbers here so the positive numbers can be used
// for task id specific listeners that will be added later.
@@ -90,8 +92,6 @@
})
public @interface TaskListenerType {}
- private static final String TAG = "ShellTaskOrganizer";
-
/**
* Callbacks for when the tasks change in the system.
*/
@@ -177,6 +177,9 @@
@Nullable
private final CompatUIController mCompatUI;
+ @NonNull
+ private final ShellCommandHandler mShellCommandHandler;
+
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
@@ -187,29 +190,33 @@
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor) {
- this(null /* shellInit */, null /* taskOrganizerController */, null /* compatUI */,
+ this(null /* shellInit */, null /* shellCommandHandler */,
+ null /* taskOrganizerController */, null /* compatUI */,
Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */,
mainExecutor);
}
public ShellTaskOrganizer(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
@Nullable CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
- this(shellInit, null /* taskOrganizerController */, compatUI,
+ this(shellInit, shellCommandHandler, null /* taskOrganizerController */, compatUI,
unfoldAnimationController, recentTasks, mainExecutor);
}
@VisibleForTesting
protected ShellTaskOrganizer(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ITaskOrganizerController taskOrganizerController,
@Nullable CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
super(taskOrganizerController, mainExecutor);
+ mShellCommandHandler = shellCommandHandler;
mCompatUI = compatUI;
mRecentTasks = recentTasks;
mUnfoldAnimationController = unfoldAnimationController.orElse(null);
@@ -219,6 +226,7 @@
}
private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
if (mCompatUI != null) {
mCompatUI.setCompatUICallback(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index d3dc7e8..0283be3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -57,6 +57,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellInit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -91,7 +92,6 @@
* Raw delta between {@link #mInitTouchLocation} and the last touch location.
*/
private final Point mTouchEventDelta = new Point();
- private final ShellExecutor mShellExecutor;
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
@@ -106,6 +106,9 @@
private final SurfaceControl.Transaction mTransaction;
private final IActivityTaskManager mActivityTaskManager;
private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final ShellExecutor mShellExecutor;
+ private final Handler mBgHandler;
@Nullable
private IOnBackInvokedCallback mBackToLauncherCallback;
private float mTriggerThreshold;
@@ -136,16 +139,19 @@
};
public BackAnimationController(
+ @NonNull ShellInit shellInit,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context) {
- this(shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
+ this(shellInit, shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
ActivityTaskManager.getService(), context, context.getContentResolver());
}
@VisibleForTesting
- BackAnimationController(@NonNull @ShellMainThread ShellExecutor shellExecutor,
- @NonNull @ShellBackgroundThread Handler handler,
+ BackAnimationController(
+ @NonNull ShellInit shellInit,
+ @NonNull @ShellMainThread ShellExecutor shellExecutor,
+ @NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull SurfaceControl.Transaction transaction,
@NonNull IActivityTaskManager activityTaskManager,
Context context, ContentResolver contentResolver) {
@@ -153,7 +159,13 @@
mTransaction = transaction;
mActivityTaskManager = activityTaskManager;
mContext = context;
- setupAnimationDeveloperSettingsObserver(contentResolver, handler);
+ mContentResolver = contentResolver;
+ mBgHandler = bgHandler;
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
}
private void setupAnimationDeveloperSettingsObserver(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 31fc6a5..2c02006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -452,6 +452,7 @@
*/
void setEntry(@NonNull final BubbleEntry entry) {
Objects.requireNonNull(entry);
+ boolean showingDotPreviously = showDot();
mLastUpdated = entry.getStatusBarNotification().getPostTime();
mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
+ if (showingDotPreviously != showDot()) {
+ // This will update the UI if needed
+ setShowDot(showDot());
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
index 4eeb207..d6803e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -19,14 +19,14 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.Color;
import android.graphics.Path;
+import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
import com.android.wm.shell.R;
/**
@@ -44,78 +44,77 @@
* will include the workprofile indicator on the badge if appropriate.
*/
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
- Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
-
if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
- userBadgedBitmap = Bitmap.createScaledBitmap(
- getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
- userBadgedAppIcon.getIntrinsicWidth()),
- mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+ AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
+ userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
+ }
+ if (isImportantConversation) {
+ userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
+ }
+ Bitmap userBadgedBitmap = createIconBitmap(
+ userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW);
+ return createIconBitmap(userBadgedBitmap);
+ }
+
+ private class CircularRingDrawable extends CircularAdaptiveIcon {
+
+ final int mImportantConversationColor;
+ final Rect mTempBounds = new Rect();
+
+ final Drawable mDr;
+
+ CircularRingDrawable(Drawable dr) {
+ super(null, null);
+ mDr = dr;
+ mImportantConversationColor = mContext.getResources().getColor(
+ R.color.important_conversation, null);
}
- if (isImportantConversation) {
- final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+ canvas.drawColor(mImportantConversationColor);
+ int ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width);
- final int importantConversationColor = mContext.getResources().getColor(
- R.color.important_conversation, null);
- Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
- userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
- Canvas c = new Canvas(badgeAndRing);
-
- Paint ringPaint = new Paint();
- ringPaint.setStyle(Paint.Style.FILL);
- ringPaint.setColor(importantConversationColor);
- ringPaint.setAntiAlias(true);
- c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
- final int bitmapTop = (int) ringStrokeWidth;
- final int bitmapLeft = (int) ringStrokeWidth;
- final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
- final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
- Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
- bitmapHeight, /* filter */ true);
- c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
- shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
- return createIconBitmap(badgeAndRing);
- } else {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
+ mTempBounds.set(getBounds());
+ mTempBounds.inset(ringStrokeWidth, ringStrokeWidth);
+ mDr.setBounds(mTempBounds);
+ mDr.draw(canvas);
+ canvas.restoreToCount(save);
}
}
- private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
- Drawable foreground = icon.getForeground();
- Drawable background = icon.getBackground();
- Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas();
- canvas.setBitmap(bitmap);
+ private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
- // Clip canvas to circle.
- Path circlePath = new Path();
- circlePath.addCircle(/* x */ size / 2f,
- /* y */ size / 2f,
- /* radius */ size / 2f,
- Path.Direction.CW);
- canvas.clipPath(circlePath);
+ final Path mPath = new Path();
- // Draw background.
- background.setBounds(0, 0, size, size);
- background.draw(canvas);
+ CircularAdaptiveIcon(Drawable bg, Drawable fg) {
+ super(bg, fg);
+ }
- // Draw foreground. The foreground and background drawables are derived from adaptive icons
- // Some icon shapes fill more space than others, so adaptive icons are normalized to about
- // the same size. This size is smaller than the original bounds, so we estimate
- // the difference in this offset.
- int offset = size / 5;
- foreground.setBounds(-offset, -offset, size + offset, size + offset);
- foreground.draw(canvas);
+ @Override
+ public Path getIconMask() {
+ mPath.reset();
+ Rect bounds = getBounds();
+ mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
+ return mPath;
+ }
- canvas.setBitmap(null);
- return bitmap;
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+
+ canvas.drawColor(Color.BLACK);
+ Drawable d;
+ if ((d = getBackground()) != null) {
+ d.draw(canvas);
+ }
+ if ((d = getForeground()) != null) {
+ d.draw(canvas);
+ }
+ canvas.restoreToCount(save);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 3982616..8771ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1055,18 +1055,28 @@
public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
// If this is an interruptive notif, mark that it's interrupted
mSysuiProxy.setNotificationInterruption(notif.getKey());
- if (!notif.getRanking().isTextChanged()
+ boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
&& (notif.getBubbleMetadata() != null
- && !notif.getBubbleMetadata().getAutoExpandBubble())
+ && !notif.getBubbleMetadata().getAutoExpandBubble());
+ if (isNonInterruptiveNotExpanding
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
- b.setEntry(notif);
+ if (notif.isBubble()) {
+ notif.setFlagBubble(false);
+ }
+ updateNotNotifyingEntry(b, notif, showInShade);
+ } else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
+ && isNonInterruptiveNotExpanding) {
+ Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
+ if (b != null) {
+ updateNotNotifyingEntry(b, notif, showInShade);
+ }
} else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
if (b != null) {
- b.setEntry(notif);
+ updateNotNotifyingEntry(b, notif, showInShade);
}
} else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
}
+ mImpl.mCachedState.updateBubbleSuppressedState(bubble);
} else {
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
}
- void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
+ void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
+ boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
+ boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
+ b.setEntry(entry);
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
+ b.setSuppressNotification(suppress);
+ b.setShowDot(!isBubbleExpandedAndSelected);
+ mImpl.mCachedState.updateBubbleSuppressedState(b);
+ }
+
+ @VisibleForTesting
+ public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@
}
@VisibleForTesting
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
+ if (!fromSystem) {
+ return;
+ }
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, shouldBubbleUp);
+ onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
}
}
}
@@ -1773,9 +1798,9 @@
}
@Override
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
mMainExecutor.execute(() -> {
- BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+ BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index 9d3bf34..5dab8a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -65,4 +66,19 @@
return null;
}
}
+
+ /**
+ * Creates the bitmap for the provided drawable and returns the scale used for
+ * drawing the actual drawable.
+ */
+ public Bitmap createIconBitmap(@NonNull Drawable icon, float[] outScale) {
+ if (outScale == null) {
+ outScale = new float[1];
+ }
+ icon = normalizeAndWrapToAdaptiveIcon(icon,
+ true /* shrinkNonAdaptiveIcons */,
+ null /* outscale */,
+ outScale);
+ return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 69762c9..f437553 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -195,15 +195,18 @@
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
// Raw badge bitmap never includes the important conversation ring
- info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
- info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
+ info.mRawBadgeBitmap = b.isImportantConversation()
+ ? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
+ : badgeBitmapInfo.icon;
+
+ float[] bubbleBitmapScale = new float[1];
+ info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
c.getResources().getString(com.android.internal.R.string.config_icon_mask));
Matrix matrix = new Matrix();
- float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
- null /* outBounds */, null /* path */, null /* outMaskShape */);
+ float scale = bubbleBitmapScale[0];
float radius = DEFAULT_PATH_SIZE / 2f;
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
radius /* pivot y */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index cf792cd..37b96ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -171,8 +171,9 @@
*
* @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
+ * @param fromSystem {@code true} if this update is from NotificationManagerService.
*/
- void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
/**
* Called when new notification entry removed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 40cf9a3..85c8ebf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -1130,16 +1130,16 @@
boolean adjusted = false;
if (mYOffsetForIme != 0) {
if (dividerLeash != null) {
- mTempRect.set(mDividerBounds);
+ getRefDividerBounds(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
}
- mTempRect.set(mBounds1);
+ getRefBounds1(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash1, mTempRect.left, mTempRect.top);
- mTempRect.set(mBounds2);
+ getRefBounds2(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash2, mTempRect.left, mTempRect.top);
adjusted = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index db8d9d4..235fd9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.lang.ref.WeakReference;
@@ -119,6 +120,7 @@
private boolean mKeyguardShowing;
public CompatUIController(Context context,
+ ShellInit shellInit,
ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -134,10 +136,14 @@
mSyncQueue = syncQueue;
mMainExecutor = mainExecutor;
mTransitionsLazy = transitionsLazy;
+ mCompatUIHintsState = new CompatUIHintsState();
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellController.addKeyguardChangeListener(this);
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
- mCompatUIHintsState = new CompatUIHintsState();
- shellController.addKeyguardChangeListener(this);
}
/** Sets the callback for UI interactions. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 81904e2..e22c951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -49,6 +49,7 @@
import com.android.wm.shell.pip.tv.TvPipTransition;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -138,12 +139,14 @@
@WMSingleton
@Provides
static PipTransitionController provideTvPipTransition(
- Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Transitions transitions,
PipAnimationController pipAnimationController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsState tvPipBoundsState, TvPipMenuController pipMenuController) {
- return new TvPipTransition(tvPipBoundsState, pipMenuController,
- tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ return new TvPipTransition(shellInit, shellTaskOrganizer, transitions, tvPipBoundsState,
+ pipMenuController, tvPipBoundsAlgorithm, pipAnimationController);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 3add417..bbaf51f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -174,13 +174,14 @@
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional,
@ShellMainThread ShellExecutor mainExecutor
) {
- return new ShellTaskOrganizer(shellInit, compatUI, unfoldAnimationController,
- recentTasksOptional, mainExecutor);
+ return new ShellTaskOrganizer(shellInit, shellCommandHandler, compatUI,
+ unfoldAnimationController, recentTasksOptional, mainExecutor);
}
@WMSingleton
@@ -188,6 +189,7 @@
static KidsModeTaskOrganizer provideKidsModeTaskOrganizer(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -196,19 +198,20 @@
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler
) {
- return new KidsModeTaskOrganizer(context, shellInit, syncTransactionQueue,
- displayController, displayInsetsController, unfoldAnimationController,
- recentTasksOptional, mainExecutor, mainHandler);
+ return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler,
+ syncTransactionQueue, displayController, displayInsetsController,
+ unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler);
}
@WMSingleton
@Provides
static CompatUIController provideCompatUIController(Context context,
+ ShellInit shellInit,
ShellController shellController,
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) {
- return new CompatUIController(context, shellController, displayController,
+ return new CompatUIController(context, shellInit, shellController, displayController,
displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy);
}
@@ -268,12 +271,14 @@
@Provides
static Optional<BackAnimationController> provideBackAnimationController(
Context context,
+ ShellInit shellInit,
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
- new BackAnimationController(shellExecutor, backgroundHandler, context));
+ new BackAnimationController(shellInit, shellExecutor, backgroundHandler,
+ context));
}
return Optional.empty();
}
@@ -384,11 +389,14 @@
@WMSingleton
@Provides
static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
- ShellController shellController, DisplayController displayController,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(
- HideDisplayCutoutController.create(context, shellController, displayController,
- mainExecutor));
+ HideDisplayCutoutController.create(context, shellInit, shellCommandHandler,
+ shellController, displayController, mainExecutor));
}
//
@@ -466,12 +474,13 @@
static Optional<RecentTasksController> provideRecentTasksController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
- RecentTasksController.create(context, shellInit, taskStackListener,
- mainExecutor));
+ RecentTasksController.create(context, shellInit, shellCommandHandler,
+ taskStackListener, mainExecutor));
}
//
@@ -649,8 +658,9 @@
@WMSingleton
@Provides
static ShellController provideShellController(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellController(shellInit, mainExecutor);
+ return new ShellController(shellInit, shellCommandHandler, mainExecutor);
}
//
@@ -676,12 +686,15 @@
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
+ Optional<Pip> pipOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents,
Optional<RecentTasksController> recentTasksOptional,
+ Optional<OneHandedController> oneHandedControllerOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
ActivityEmbeddingController activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
@@ -697,18 +710,7 @@
@WMSingleton
@Provides
- static ShellCommandHandler provideShellCommandHandlerImpl(
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHandedController> oneHandedOptional,
- Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<RecentTasksController> recentTasksOptional,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new ShellCommandHandler(shellController, shellTaskOrganizer,
- kidsModeTaskOrganizer, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, recentTasksOptional, mainExecutor);
+ static ShellCommandHandler provideShellCommandHandler() {
+ return new ShellCommandHandler();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e2bf767..2ca9c3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -74,6 +74,7 @@
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
@@ -248,14 +249,20 @@
@Provides
@DynamicOverride
static OneHandedController provideOneHandedController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
- WindowManager windowManager, DisplayController displayController,
- DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
- @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
- return OneHandedController.create(context, shellController, windowManager,
- displayController, displayLayout, taskStackListener, jankMonitor, uiEventLogger,
- mainExecutor, mainHandler);
+ WindowManager windowManager,
+ DisplayController displayController,
+ DisplayLayout displayLayout,
+ TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ InteractionJankMonitor jankMonitor,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return OneHandedController.create(context, shellInit, shellCommandHandler, shellController,
+ windowManager, displayController, displayLayout, taskStackListener, jankMonitor,
+ uiEventLogger, mainExecutor, mainHandler);
}
//
@@ -268,6 +275,7 @@
static SplitScreenController provideSplitScreenController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
@@ -281,10 +289,10 @@
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
@ShellMainThread ShellExecutor mainExecutor) {
- return new SplitScreenController(context, shellInit, shellController, shellTaskOrganizer,
- syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController,
- displayInsetsController, dragAndDropController, transitions, transactionPool,
- iconProvider, recentTasks, mainExecutor);
+ return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
+ shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
+ displayImeController, displayInsetsController, dragAndDropController, transitions,
+ transactionPool, iconProvider, recentTasks, mainExecutor);
}
//
@@ -294,24 +302,33 @@
@WMSingleton
@Provides
static Optional<Pip> providePip(Context context,
- ShellController shellController, DisplayController displayController,
- PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
- PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
- PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper,
+ PipMediaController pipMediaController,
+ PhonePipMenuController phonePipMenuController,
+ PipTaskOrganizer pipTaskOrganizer,
PipTransitionState pipTransitionState,
- PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+ PipTouchHandler pipTouchHandler,
+ PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(PipController.create(context, shellController, displayController,
- pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
- pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
+ return Optional.ofNullable(PipController.create(
+ context, shellInit, shellCommandHandler, shellController,
+ displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+ pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+ pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ oneHandedController, mainExecutor));
}
@WMSingleton
@@ -409,15 +426,15 @@
@WMSingleton
@Provides
static PipTransitionController providePipTransitionController(Context context,
- Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
PhonePipMenuController pipMenuController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
- return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
- pipSurfaceTransactionHelper, splitScreenOptional);
+ return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+ pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm,
+ pipAnimationController, pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 52f0c42..99922fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -59,9 +59,9 @@
adb shell dumpsys activity service SystemUIService WMShell
```
-If information should be added to the dump, make updates to:
-- `WMShell` if you are dumping SysUI state
-- `ShellCommandHandler` if you are dumping Shell state
+If information should be added to the dump, either:
+- Update `WMShell` if you are dumping SysUI state
+- Inject `ShellCommandHandler` into your Shell class, and add a dump callback
## Debugging in Android Studio
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
index 0dd50b1..d6302e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
@@ -32,7 +32,7 @@
More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).
-## Interfaces to Shell components
+## Interfaces from SysUI to Shell components
Within the same process, the WM Shell components can be running on a different thread than the main
SysUI thread (disabled on certain products). This introduces challenges where we have to be
@@ -54,12 +54,30 @@
Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently
necessary to maintain proper threading and logic isolation.
-## Configuration changes & other SysUI events
+## Listening for Configuration changes & other SysUI events
-Aside from direct calls into Shell controllers for exposed features, the Shell also receives
+Aside from direct calls into Shell controllers for exposed features, the Shell also receives
common event callbacks from SysUI via the `ShellController`. This includes things like:
- Configuration changes
-- TODO: Shell init
-- TODO: Shell command
-- TODO: Keyguard events
\ No newline at end of file
+- Keyguard events
+- Shell init
+- Shell dumps & commands
+
+For other events which are specific to the Shell feature, then you can add callback methods on
+the Shell feature interface. Any such calls should <u>**never**</u> be synchronous calls as
+they will need to post to the Shell main thread to run.
+
+## Shell commands & Dumps
+
+Since the Shell library is a part of the SysUI process, it relies on SysUI to trigger commands
+on individual Shell components, or to dump individual shell components.
+
+```shell
+# Dump everything
+adb shell dumpsys activity service SystemUIService WMShell
+
+# Run a specific command
+adb shell dumpsys activity service SystemUIService WMShell help
+adb shell dumpsys activity service SystemUIService WMShell <cmd> <args> ...
+```
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 20d7725..af205ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -122,7 +122,6 @@
break;
case WindowManager.TRANSIT_TO_BACK:
case WindowManager.TRANSIT_TO_FRONT:
- transitionHandled = true;
break;
}
}
@@ -147,7 +146,9 @@
return false;
}
mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
- return true;
+
+ // Intercepted transition to manage the window decorations. Let other handlers animate.
+ return false;
}
private boolean startCloseTransition(
@@ -164,7 +165,8 @@
windowDecors.add(windowDecor);
}
- return true;
+ // Intercepted transition to manage the window decorations. Let other handlers animate.
+ return false;
}
private boolean startChangeTransition(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 665b035..32125fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -27,7 +27,9 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
@@ -38,6 +40,7 @@
private static final String TAG = "HideDisplayCutoutController";
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final HideDisplayCutoutOrganizer mOrganizer;
@VisibleForTesting
@@ -49,7 +52,10 @@
*/
@Nullable
public static HideDisplayCutoutController create(Context context,
- ShellController shellController, DisplayController displayController,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
@@ -60,14 +66,24 @@
HideDisplayCutoutOrganizer organizer =
new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
- return new HideDisplayCutoutController(context, shellController, organizer);
+ return new HideDisplayCutoutController(context, shellInit, shellCommandHandler,
+ shellController, organizer);
}
- HideDisplayCutoutController(Context context, ShellController shellController,
+ HideDisplayCutoutController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
HideDisplayCutoutOrganizer organizer) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mOrganizer = organizer;
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
updateStatus();
mShellController.addConfigurationChangeListener(this);
}
@@ -96,11 +112,11 @@
updateStatus();
}
- public void dump(@NonNull PrintWriter pw) {
- final String prefix = " ";
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = " ";
pw.print(TAG);
pw.println(" states: ");
- pw.print(prefix);
+ pw.print(innerPrefix);
pw.print("mEnabled=");
pw.println(mEnabled);
mOrganizer.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 73b9b89..2fdd121 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -73,6 +74,7 @@
private final Handler mMainHandler;
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final SyncTransactionQueue mSyncQueue;
private final DisplayController mDisplayController;
private final DisplayInsetsController mDisplayInsetsController;
@@ -141,6 +143,8 @@
@VisibleForTesting
KidsModeTaskOrganizer(
Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ITaskOrganizerController taskOrganizerController,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
@@ -150,19 +154,23 @@
KidsModeSettingsObserver kidsModeSettingsObserver,
ShellExecutor mainExecutor,
Handler mainHandler) {
- super(/* shellInit= */ null, taskOrganizerController, /* compatUI= */ null,
- unfoldAnimationController, recentTasks, mainExecutor);
+ // Note: we don't call super with the shell init because we will be initializing manually
+ super(/* shellInit= */ null, /* shellCommandHandler= */ null, taskOrganizerController,
+ /* compatUI= */ null, unfoldAnimationController, recentTasks, mainExecutor);
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
mKidsModeSettingsObserver = kidsModeSettingsObserver;
+ shellInit.addInitCallback(this::onInit, this);
}
public KidsModeTaskOrganizer(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -171,9 +179,10 @@
ShellExecutor mainExecutor,
Handler mainHandler) {
// Note: we don't call super with the shell init because we will be initializing manually
- super(/* shellInit= */ null, /* compatUI= */ null, unfoldAnimationController, recentTasks,
- mainExecutor);
+ super(/* shellInit= */ null, /* taskOrganizerController= */ null, /* compatUI= */ null,
+ unfoldAnimationController, recentTasks, mainExecutor);
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
mDisplayController = displayController;
@@ -185,6 +194,9 @@
* Initializes kids mode status.
*/
public void onInit() {
+ if (mShellCommandHandler != null) {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ }
if (mKidsModeSettingsObserver == null) {
mKidsModeSettingsObserver = new KidsModeSettingsObserver(mMainHandler, mContext);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 24f02ac..9149204 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -56,7 +56,9 @@
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
@@ -85,6 +87,7 @@
private Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final AccessibilityManager mAccessibilityManager;
private final DisplayController mDisplayController;
@@ -192,8 +195,9 @@
/**
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
- public static OneHandedController create(
- Context context, ShellController shellController, WindowManager windowManager,
+ public static OneHandedController create(Context context,
+ ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+ ShellController shellController, WindowManager windowManager,
DisplayController displayController, DisplayLayout displayLayout,
TaskStackListenerImpl taskStackListener,
InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
@@ -213,14 +217,16 @@
context, displayLayout, settingsUtil, animationController, tutorialHandler,
jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
- return new OneHandedController(context, shellController, displayController, organizer,
- touchHandler, tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler,
- oneHandedState, oneHandedUiEventsLogger, taskStackListener,
- mainExecutor, mainHandler);
+ return new OneHandedController(context, shellInit, shellCommandHandler, shellController,
+ displayController, organizer, touchHandler, tutorialHandler, settingsUtil,
+ accessibilityUtil, timeoutHandler, oneHandedState, oneHandedUiEventsLogger,
+ taskStackListener, mainExecutor, mainHandler);
}
@VisibleForTesting
OneHandedController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
@@ -235,6 +241,7 @@
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mOneHandedSettingsUtil = settingsUtil;
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
@@ -247,8 +254,8 @@
mMainHandler = mainHandler;
mOneHandedUiEventLogger = uiEventsLogger;
mTaskStackListener = taskStackListener;
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
- mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
@@ -268,6 +275,12 @@
getObserver(this::onSwipeToNotificationEnabledChanged);
mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
mDisplayController.addDisplayChangingController(this);
setupCallback();
registerSettingObservers(mUserId);
@@ -275,7 +288,6 @@
updateSettings();
updateDisplayLayout(mContext.getDisplayId());
- mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -623,7 +635,7 @@
updateOneHandedEnabled();
}
- public void dump(@NonNull PrintWriter pw) {
+ public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = " ";
pw.println();
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 38631ce..93172f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -20,7 +20,6 @@
import com.android.wm.shell.common.annotations.ExternalThread;
-import java.io.PrintWriter;
import java.util.function.Consumer;
/**
@@ -99,12 +98,4 @@
* view hierarchy or destroyed.
*/
default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
-
- /**
- * Dump the current state and information if need.
- *
- * @param pw The stream to dump information to.
- */
- default void dump(PrintWriter pw) {
- }
}
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 cf2734c..81e49f8 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
@@ -183,7 +183,7 @@
return mCurrentAnimator;
}
- PipTransitionAnimator getCurrentAnimator() {
+ public PipTransitionAnimator getCurrentAnimator() {
return mCurrentAnimator;
}
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 1155ea1..f747b5e 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
@@ -127,7 +127,7 @@
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
- private final PipTransitionController mPipTransitionController;
+ protected final PipTransitionController mPipTransitionController;
protected final PipParamsChangedForwarder mPipParamsChangedForwarder;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterAnimationDuration;
@@ -196,6 +196,26 @@
}
};
+ @VisibleForTesting
+ final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
+ new PipTransitionController.PipTransitionCallback() {
+ @Override
+ public void onPipTransitionStarted(int direction, Rect pipBounds) {}
+
+ @Override
+ public void onPipTransitionFinished(int direction) {
+ // Apply the deferred RunningTaskInfo if applicable after all proper callbacks
+ // are sent.
+ if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
+ onTaskInfoChanged(mDeferredTaskInfo);
+ mDeferredTaskInfo = null;
+ }
+ }
+
+ @Override
+ public void onPipTransitionCanceled(int direction) {}
+ };
+
private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
new PipAnimationController.PipTransactionHandler() {
@Override
@@ -216,7 +236,7 @@
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
- private PipTransitionState mPipTransitionState;
+ protected PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -296,6 +316,7 @@
mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
+ pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
}
public PipTransitionController getTransitionController() {
@@ -773,11 +794,6 @@
mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
- // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
- if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
- onTaskInfoChanged(mDeferredTaskInfo);
- mDeferredTaskInfo = null;
- }
}
private void sendOnPipTransitionCancelled(
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 51be2a5..33761d2 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
@@ -66,6 +66,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
@@ -107,17 +108,18 @@
private boolean mHasFadeOut;
public PipTransition(Context context,
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
- Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
- super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
- pipAnimationController, transitions, shellTaskOrganizer);
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController);
mContext = context;
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
@@ -315,7 +317,8 @@
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
if (transition != mExitTransition) {
return;
}
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 90a2695..f51e247 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
@@ -38,6 +38,7 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
@@ -131,10 +132,13 @@
public void onFixedRotationStarted() {
}
- public PipTransitionController(PipBoundsState pipBoundsState,
+ public PipTransitionController(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController, Transitions transitions,
- @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ PipAnimationController pipAnimationController) {
mPipBoundsState = pipBoundsState;
mPipMenuController = pipMenuController;
mShellTaskOrganizer = shellTaskOrganizer;
@@ -142,10 +146,14 @@
mPipAnimationController = pipAnimationController;
mTransitions = transitions;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- transitions.addHandler(this);
+ shellInit.addInitCallback(this::onInit, this);
}
}
+ private void onInit() {
+ mTransitions.addHandler(this);
+ }
+
void setPipOrganizer(PipTaskOrganizer pto) {
mPipOrganizer = pto;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 586e3a0..fc97f31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -89,7 +89,9 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -122,6 +124,7 @@
private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder;
private Optional<OneHandedController> mOneHandedController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
protected final PipImpl mImpl;
@@ -295,6 +298,8 @@
*/
@Nullable
public static Pip create(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
@@ -319,16 +324,18 @@
return null;
}
- return new PipController(context, shellController, displayController, pipAppOpsListener,
- pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController,
+ return new PipController(context, shellInit, shellCommandHandler, shellController,
+ displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+ pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+ pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
oneHandedController, mainExecutor)
.mImpl;
}
protected PipController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
@@ -355,6 +362,7 @@
}
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
@@ -378,11 +386,11 @@
.getInteger(R.integer.config_pipEnterAnimationDuration);
mPipParamsChangedForwarder = pipParamsChangedForwarder;
- //TODO: move this to ShellInit when PipController can be injected
- mMainExecutor.execute(this::init);
+ shellInit.addInitCallback(this::onInit, this);
}
- public void init() {
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
INPUT_CONSUMER_PIP, mMainExecutor);
mPipTransitionController.registerPipTransitionCallback(this);
@@ -503,6 +511,12 @@
updateMovementBounds(null /* toBounds */, false /* fromRotation */,
false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
null /* windowContainerTransaction */);
+ } else {
+ // when we enter pip for the first time, the destination bounds and pip
+ // bounds will already match, since they are calculated prior to
+ // starting the animation, so we only need to update the min/max size
+ // that is used for e.g. double tap to maximized state
+ mTouchHandler.updateMinMaxSize(ratio);
}
}
@@ -622,7 +636,8 @@
mPipTaskOrganizer.scheduleAnimateResizePip(
postChangeBounds, duration, null /* updateBoundsCallback */);
} else {
- mTouchHandler.getMotionHelper().movePip(postChangeBounds);
+ // Directly move PiP to its final destination bounds without animation.
+ mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds);
}
} else {
updateDisplayLayout.run();
@@ -912,7 +927,7 @@
return true;
}
- private void dump(PrintWriter pw) {
+ private void dump(PrintWriter pw, String prefix) {
final String innerPrefix = " ";
pw.println(TAG);
mMenuController.dump(pw, innerPrefix);
@@ -1000,18 +1015,6 @@
PipController.this.showPictureInPictureMenu();
});
}
-
- @Override
- public void dump(PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> {
- PipController.this.dump(pw);
- });
- } catch (InterruptedException e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Failed to dump PipController in 2s", TAG);
- }
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c86c136..a2fa058 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -412,13 +412,7 @@
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
- if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
- } else {
- mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
- mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
- mPipBoundsState.getExpandedBounds().height());
- }
+ updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -487,6 +481,27 @@
}
}
+ /**
+ * Update the values for min/max allowed size of picture in picture window based on the aspect
+ * ratio.
+ * @param aspectRatio aspect ratio to use for the calculation of min/max size
+ */
+ public void updateMinMaxSize(float aspectRatio) {
+ updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+ aspectRatio);
+ }
+
+ private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+ float aspectRatio) {
+ if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
+ updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ } else {
+ mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+ mPipBoundsState.getExpandedBounds().height());
+ }
+ }
+
private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
float aspectRatio) {
final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81..b212ea9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -122,9 +122,9 @@
cancelScheduledPlacement();
applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
} else if (immediate) {
+ boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
cancelScheduledPlacement();
- applyPlacementBounds(placement.getBounds(), animationDuration);
- scheduleUnstashIfNeeded(placement);
+ applyPlacement(placement, shouldStash, animationDuration);
} else {
applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
schedulePinnedStackPlacement(placement, animationDuration);
@@ -176,22 +176,21 @@
"%s: applyPendingPlacement()", TAG);
}
if (mPendingPlacement != null) {
- if (mPendingStash) {
- mPendingStash = false;
- scheduleUnstashIfNeeded(mPendingPlacement);
- }
+ applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+ mPendingStash = false;
+ mPendingPlacement = null;
+ }
+ }
- if (mUnstashRunnable != null) {
- // currently stashed, use stashed pos
- applyPlacementBounds(mPendingPlacement.getBounds(),
- mPendingPlacementAnimationDuration);
- } else {
- applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
- mPendingPlacementAnimationDuration);
- }
+ private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+ int animationDuration) {
+ if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+ scheduleUnstashIfNeeded(placement);
}
- mPendingPlacement = null;
+ Rect bounds =
+ mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+ applyPlacementBounds(bounds, animationDuration);
}
void onPipDismissed() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 5062cc4..8ebcf63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
/**
@@ -39,14 +40,16 @@
* TODO: Implement animation once TV is using Transitions.
*/
public class TvPipTransition extends PipTransitionController {
- public TvPipTransition(PipBoundsState pipBoundsState,
+ public TvPipTransition(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
- PipAnimationController pipAnimationController,
- Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer) {
- super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
- transitions, shellTaskOrganizer);
+ PipAnimationController pipAnimationController) {
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ tvPipBoundsAlgorithm, pipAnimationController);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 3d1a7e9..7b42350 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -44,6 +44,7 @@
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -62,6 +63,7 @@
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
@@ -87,20 +89,24 @@
public static RecentTasksController create(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
- return new RecentTasksController(context, shellInit, taskStackListener, mainExecutor);
+ return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
+ mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mMainExecutor = mainExecutor;
@@ -112,6 +118,7 @@
}
private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index de7e7bd..2117b69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -21,6 +21,7 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -83,6 +84,7 @@
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -90,7 +92,6 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -131,6 +132,7 @@
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
@@ -147,6 +149,7 @@
private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
@@ -155,6 +158,7 @@
public SplitScreenController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
@@ -168,6 +172,7 @@
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -183,6 +188,7 @@
mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
+ mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
@@ -200,6 +206,9 @@
*/
@VisibleForTesting
void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler,
+ this);
mShellController.addKeyguardChangeListener(this);
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -337,17 +346,39 @@
}
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ final int[] result = new int[1];
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
+ }
+ if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+ mSyncQueue.queue(evictWct);
+ }
+ }
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ }
+ };
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+ 0 /* duration */, 0 /* statusBarTransitionDelay */);
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
try {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- mStageCoordinator.prepareEvictChildTasks(position, evictWct);
- final int result =
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
- if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
- mSyncQueue.queue(evictWct);
- }
+ result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
+ activityOptions.toBundle());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
@@ -403,7 +434,7 @@
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
// split.
- if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
@@ -418,8 +449,7 @@
/** Returns {@code true} if it's launching the same component on both sides of the split. */
@VisibleForTesting
- boolean isLaunchingAdjacently(@Nullable Intent startIntent,
- @SplitPosition int position) {
+ boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
if (startIntent == null) {
return false;
}
@@ -430,6 +460,16 @@
}
if (isSplitScreenVisible()) {
+ // To prevent users from constantly dropping the same app to the same side resulting in
+ // a large number of instances in the background.
+ final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
+ final ComponentName targetActivity = targetTaskInfo != null
+ ? targetTaskInfo.baseIntent.getComponent() : null;
+ if (Objects.equals(launchingActivity, targetActivity)) {
+ return false;
+ }
+
+ // Allow users to start a new instance the same to adjacent side.
final ActivityManager.RunningTaskInfo pairedTaskInfo =
getTaskInfo(SplitLayout.reversePosition(position));
final ComponentName pairedActivity = pairedTaskInfo != null
@@ -453,12 +493,12 @@
mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
mSyncQueue.queue(wct);
}
- return reparentSplitTasksForAnimation(apps, true /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
try {
- return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
} finally {
for (RemoteAnimationTarget appTarget : apps) {
if (appTarget.leash != null) {
@@ -469,14 +509,23 @@
}
private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
- boolean splitExpectedToBeVisible) {
+ boolean enterSplitScreen) {
if (ENABLE_SHELL_TRANSITIONS) return null;
- // TODO(b/206487881): Integrate this with shell transition.
- if (splitExpectedToBeVisible && !isSplitScreenVisible()) return null;
- // Split not visible, but not enough apps to have split, also return null
- if (!splitExpectedToBeVisible && apps.length < 2) return null;
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ if (enterSplitScreen) {
+ int openingApps = 0;
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) openingApps++;
+ }
+ if (openingApps < 2) {
+ // Not having enough apps to enter split screen
+ return null;
+ }
+ } else if (!isSplitScreenVisible()) {
+ return null;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
if (mSplitTasksContainerLayer != null) {
// Remove the previous layer before recreating
transaction.remove(mSplitTasksContainerLayer);
@@ -489,17 +538,14 @@
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
mSplitTasksContainerLayer = builder.build();
- // Ensure that we order these in the parent in the right z-order as their previous order
- Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
- int layer = 1;
- for (RemoteAnimationTarget appTarget : apps) {
+ for (int i = 0; i < apps.length; ++i) {
+ final RemoteAnimationTarget appTarget = apps[i];
transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
- transaction.setLayer(appTarget.leash, layer++);
}
transaction.apply();
- transaction.close();
+ mTransactionPool.release(transaction);
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
new file mode 100644
index 0000000..681d964
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
@@ -0,0 +1,96 @@
+/*
+ * 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.splitscreen;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+
+import com.android.wm.shell.sysui.ShellCommandHandler;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles the shell commands for the SplitscreenController.
+ */
+public class SplitScreenShellCommandHandler implements
+ ShellCommandHandler.ShellCommandActionHandler {
+
+ private final SplitScreenController mController;
+
+ public SplitScreenShellCommandHandler(SplitScreenController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public boolean onShellCommand(String[] args, PrintWriter pw) {
+ switch (args[0]) {
+ case "moveToSideStage":
+ return runMoveToSideStage(args, pw);
+ case "removeFromSideStage":
+ return runRemoveFromSideStage(args, pw);
+ case "setSideStagePosition":
+ return runSetSideStagePosition(args, pw);
+ default:
+ pw.println("Invalid command: " + args[0]);
+ return false;
+ }
+ }
+
+ private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First argument is the action name.
+ pw.println("Error: task id should be provided as arguments");
+ return false;
+ }
+ final int taskId = new Integer(args[1]);
+ final int sideStagePosition = args.length > 3
+ ? new Integer(args[2]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ mController.moveToSideStage(taskId, sideStagePosition);
+ return true;
+ }
+
+ private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // First argument is the action name.
+ pw.println("Error: task id should be provided as arguments");
+ return false;
+ }
+ final int taskId = new Integer(args[1]);
+ mController.removeFromSideStage(taskId);
+ return true;
+ }
+
+ private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // First argument is the action name.
+ pw.println("Error: side stage position should be provided as arguments");
+ return false;
+ }
+ final int position = new Integer(args[1]);
+ mController.setSideStagePosition(position);
+ return true;
+ }
+
+ @Override
+ public void printShellCommandHelp(PrintWriter pw, String prefix) {
+ pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>");
+ pw.println(prefix + " Move a task with given id in split-screen mode.");
+ pw.println(prefix + "removeFromSideStage <taskId>");
+ pw.println(prefix + " Remove a task with given id in split-screen mode.");
+ pw.println(prefix + "setSideStagePosition <SideStagePosition>");
+ pw.println(prefix + " Sets the position of the side-stage.");
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 83bdf8b..d7ca791 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -94,6 +94,7 @@
@NonNull WindowContainerToken topRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
+ mFinishTransaction = finishTransaction;
if (mPendingRemoteHandler != null) {
mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
finishTransaction, mRemoteFinishCB);
@@ -107,8 +108,6 @@
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
- mFinishTransaction = mTransactionPool.acquire();
-
// Play some place-holder fade animations
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -287,16 +286,14 @@
return true;
}
- void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
if (isPendingEnter(transition)) {
if (!aborted) {
// An enter transition got merged, appends the rest operations to finish entering
// split screen.
- // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
- if (mFinishTransaction == null) {
- mFinishTransaction = mTransactionPool.acquire();
- }
- mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ mStageCoordinator.finishEnterSplitScreen(finishT);
+ mPendingRemoteHandler = null;
}
mPendingEnter.mCallback.onTransitionConsumed(aborted);
@@ -339,11 +336,6 @@
mAnimatingTransition = null;
mOnFinish.run();
- if (mFinishTransaction != null) {
- mFinishTransaction.apply();
- mTransactionPool.release(mFinishTransaction);
- mFinishTransaction = null;
- }
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
mFinishCallback = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index f2340d5..8e1ae39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -233,6 +233,7 @@
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
+ updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
@@ -451,10 +452,16 @@
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
- // Switch the split position if launching as MULTIPLE_TASK failed.
- if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
- setSideStagePosition(SplitLayout.reversePosition(
- getSideStagePosition()), null);
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ mMainExecutor.execute(() ->
+ exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ } else {
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+ setSideStagePosition(SplitLayout.reversePosition(
+ getSideStagePosition()), null);
+ }
}
// Do nothing when the animation was cancelled.
@@ -650,7 +657,7 @@
mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
- // multi-instagce, we should exit split and expand that app as full screen.
+ // multi-instance, we should exit split and expand that app as full screen.
if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
@@ -1742,8 +1749,9 @@
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
- mSplitTransitions.onTransitionConsumed(transition, aborted);
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
+ mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f414d69..1af9415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -33,6 +33,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -376,7 +377,13 @@
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
mSyncQueue.runInSync(t -> {
- t.setWindowCrop(leash, null);
+ // The task surface might be released before running in the sync queue for the case like
+ // trampoline launch, so check if the surface is valid before processing it.
+ if (!leash.isValid()) {
+ Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
+ return;
+ }
+ t.setCrop(leash, null);
t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
t.setAlpha(leash, 1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
index f4fc0c4..2e6ddc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
@@ -16,19 +16,14 @@
package com.android.wm.shell.sysui;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
-import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
-import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
-import java.util.Optional;
+import java.util.Arrays;
+import java.util.TreeMap;
+import java.util.function.BiConsumer;
/**
* An entry point into the shell for dumping shell internal state and running adb commands.
@@ -38,52 +33,61 @@
public final class ShellCommandHandler {
private static final String TAG = ShellCommandHandler.class.getSimpleName();
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<Pip> mPipOptional;
- private final Optional<OneHandedController> mOneHandedOptional;
- private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
- private final Optional<RecentTasksController> mRecentTasks;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
+ // We're using a TreeMap to keep them sorted by command name
+ private final TreeMap<String, BiConsumer<PrintWriter, String>> mDumpables = new TreeMap<>();
+ private final TreeMap<String, ShellCommandActionHandler> mCommands = new TreeMap<>();
- public ShellCommandHandler(
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHandedController> oneHandedOptional,
- Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<RecentTasksController> recentTasks,
- ShellExecutor mainExecutor) {
- mShellTaskOrganizer = shellTaskOrganizer;
- mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
- mRecentTasks = recentTasks;
- mSplitScreenOptional = splitScreenOptional;
- mPipOptional = pipOptional;
- mOneHandedOptional = oneHandedOptional;
- mHideDisplayCutout = hideDisplayCutout;
- // TODO(238217847): To be removed once the command handler dependencies are inverted
- shellController.setShellCommandHandler(this);
+ public interface ShellCommandActionHandler {
+ /**
+ * Handles the given command.
+ *
+ * @param args the arguments starting with the action name, then the action arguments
+ * @param pw the write to print output to
+ */
+ boolean onShellCommand(String[] args, PrintWriter pw);
+
+ /**
+ * Prints the help for this class of commands. Implementations do not need to print the
+ * command class.
+ */
+ void printShellCommandHelp(PrintWriter pw, String prefix);
+ }
+
+
+ /**
+ * Adds a callback to run when the Shell is being dumped.
+ *
+ * @param callback the callback to be made when Shell is dumped, takes the print writer and
+ * a prefix
+ * @param instance used for debugging only
+ */
+ public <T> void addDumpCallback(BiConsumer<PrintWriter, String> callback, T instance) {
+ mDumpables.put(instance.getClass().getSimpleName(), callback);
+ ProtoLog.v(WM_SHELL_INIT, "Adding dump callback for %s",
+ instance.getClass().getSimpleName());
+ }
+
+ /**
+ * Adds an action callback to be invoked when the user runs that particular command from adb.
+ *
+ * @param commandClass the top level class of command to invoke
+ * @param actions the interface to callback when an action of this class is invoked
+ * @param instance used for debugging only
+ */
+ public <T> void addCommandCallback(String commandClass, ShellCommandActionHandler actions,
+ T instance) {
+ mCommands.put(commandClass, actions);
+ ProtoLog.v(WM_SHELL_INIT, "Adding command class %s for %s", commandClass,
+ instance.getClass().getSimpleName());
}
/** Dumps WM Shell internal state. */
public void dump(PrintWriter pw) {
- mShellTaskOrganizer.dump(pw, "");
- pw.println();
- pw.println();
- mPipOptional.ifPresent(pip -> pip.dump(pw));
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
- mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
- pw.println();
- pw.println();
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
- pw.println();
- pw.println();
- mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
- pw.println();
- pw.println();
- mKidsModeTaskOrganizer.dump(pw, "");
+ for (String key : mDumpables.keySet()) {
+ final BiConsumer<PrintWriter, String> r = mDumpables.get(key);
+ r.accept(pw, "");
+ pw.println();
+ }
}
@@ -93,72 +97,32 @@
// Argument at position 0 is "WMShell".
return false;
}
- switch (args[1]) {
- case "moveToSideStage":
- return runMoveToSideStage(args, pw);
- case "removeFromSideStage":
- return runRemoveFromSideStage(args, pw);
- case "setSideStagePosition":
- return runSetSideStagePosition(args, pw);
- case "help":
- return runHelp(pw);
- default:
- return false;
- }
- }
- private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as arguments");
+ final String cmdClass = args[1];
+ if (cmdClass.toLowerCase().equals("help")) {
+ return runHelp(pw);
+ }
+ if (!mCommands.containsKey(cmdClass)) {
return false;
}
- final int taskId = new Integer(args[2]);
- final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
- mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
- return true;
- }
- private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as arguments");
- return false;
- }
- final int taskId = new Integer(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId));
- return true;
- }
-
- private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: side stage position should be provided as arguments");
- return false;
- }
- final int position = new Integer(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position));
+ // Only pass the actions onwards as arguments to the callback
+ final ShellCommandActionHandler actions = mCommands.get(args[1]);
+ final String[] cmdClassArgs = Arrays.copyOfRange(args, 2, args.length);
+ actions.onShellCommand(cmdClassArgs, pw);
return true;
}
private boolean runHelp(PrintWriter pw) {
pw.println("Window Manager Shell commands:");
+ for (String commandClass : mCommands.keySet()) {
+ pw.println(" " + commandClass);
+ mCommands.get(commandClass).printShellCommandHelp(pw, " ");
+ }
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" <no arguments provided>");
- pw.println(" Dump Window Manager Shell internal state");
- pw.println(" pair <taskId1> <taskId2>");
- pw.println(" unpair <taskId>");
- pw.println(" Pairs/unpairs tasks with given ids.");
- pw.println(" moveToSideStage <taskId> <SideStagePosition>");
- pw.println(" Move a task with given id in split-screen mode.");
- pw.println(" removeFromSideStage <taskId>");
- pw.println(" Remove a task with given id in split-screen mode.");
- pw.println(" setSideStageOutline <true/false>");
- pw.println(" Enable/Disable outline on the side-stage.");
- pw.println(" setSideStagePosition <SideStagePosition>");
- pw.println(" Sets the position of the side-stage.");
+ pw.println(" Dump all Window Manager Shell internal state");
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index f1f317f..52ffb46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -45,11 +45,10 @@
private static final String TAG = ShellController.class.getSimpleName();
private final ShellInit mShellInit;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mMainExecutor;
private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl();
- private ShellCommandHandler mShellCommandHandler;
-
private final CopyOnWriteArrayList<ConfigurationChangeListener> mConfigChangeListeners =
new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners =
@@ -57,8 +56,10 @@
private Configuration mLastConfiguration;
- public ShellController(ShellInit shellInit, ShellExecutor mainExecutor) {
+ public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+ ShellExecutor mainExecutor) {
mShellInit = shellInit;
+ mShellCommandHandler = shellCommandHandler;
mMainExecutor = mainExecutor;
}
@@ -70,15 +71,6 @@
}
/**
- * Sets the command handler to call back to.
- * TODO(238217847): This is only exposed this way until we can remove the dependencies from the
- * command handler to other classes.
- */
- public void setShellCommandHandler(ShellCommandHandler shellCommandHandler) {
- mShellCommandHandler = shellCommandHandler;
- }
-
- /**
* Adds a new configuration listener. The configuration change callbacks are not made in any
* particular order.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index c250e03..ac52235 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -52,6 +52,9 @@
* Adds a callback to the ordered list of callbacks be made when Shell is first started. This
* can be used in class constructors when dagger is used to ensure that the initialization order
* matches the dependency order.
+ *
+ * @param r the callback to be made when Shell is initialized
+ * @param instance used for debugging only
*/
public <T extends Object> void addInitCallback(Runnable r, T instance) {
if (mHasInitialized) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 11b453c..5cce6b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -274,7 +274,8 @@
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
MixedTransition mixed = null;
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
if (mActiveTransitions.get(i).mTransition != transition) continue;
@@ -283,7 +284,7 @@
}
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
- mPipHandler.onTransitionConsumed(transition, aborted);
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index cedb340..9469529 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -82,7 +82,8 @@
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
mRequestedRemotes.remove(transition);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index cd29741..279d57a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -159,8 +159,10 @@
private void onInit() {
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
ContentResolver resolver = mContext.getContentResolver();
mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
@@ -206,7 +208,13 @@
* @see TransitionHandler
*/
public void addHandler(@NonNull TransitionHandler handler) {
+ if (mHandlers.isEmpty()) {
+ throw new RuntimeException("Unexpected handler added prior to initialization, please "
+ + "use ShellInit callbacks to ensure proper ordering");
+ }
mHandlers.add(handler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+ handler.getClass().getSimpleName());
}
public ShellExecutor getMainExecutor() {
@@ -535,7 +543,8 @@
active.mMerged = true;
active.mAborted = abort;
if (active.mHandler != null) {
- active.mHandler.onTransitionConsumed(active.mToken, abort);
+ active.mHandler.onTransitionConsumed(
+ active.mToken, abort, abort ? null : active.mFinishT);
}
return;
}
@@ -543,7 +552,8 @@
active.mAborted = abort;
if (active.mAborted && active.mHandler != null) {
// Notifies to clean-up the aborted transition.
- active.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ active.mHandler.onTransitionConsumed(
+ transition, true /* aborted */, null /* finishTransaction */);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
@@ -579,7 +589,8 @@
ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
// Notifies to clean-up the aborted transition.
if (aborted.mHandler != null) {
- aborted.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ aborted.mHandler.onTransitionConsumed(
+ transition, true /* aborted */, null /* finishTransaction */);
}
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
}
@@ -765,8 +776,13 @@
* Called when a transition which was already "claimed" by this handler has been merged
* into another animation or has been aborted. Gives this handler a chance to clean-up any
* expectations.
+ *
+ * @param transition The transition been consumed.
+ * @param aborted Whether the transition is aborted or not.
+ * @param finishTransaction The transaction to be applied after the transition animated.
*/
- default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { }
+ default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) { }
/**
* Sets transition animation scale settings value to handler.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 4380bdc..506a4c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -66,6 +66,7 @@
final DisplayController mDisplayController;
final ShellTaskOrganizer mTaskOrganizer;
final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+ final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -102,7 +103,8 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface) {
this(context, displayController, taskOrganizer, taskInfo, taskSurface,
- SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
+ SurfaceControl.Builder::new, WindowContainerTransaction::new,
+ new SurfaceControlViewHostFactory() {});
}
WindowDecoration(
@@ -112,6 +114,7 @@
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
mContext = context;
mDisplayController = displayController;
@@ -119,6 +122,7 @@
mTaskInfo = taskInfo;
mTaskSurface = taskSurface;
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+ mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
@@ -229,7 +233,9 @@
mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
- .setColor(mTaskBackgroundSurface, mTmpColor);
+ .setColor(mTaskBackgroundSurface, mTmpColor)
+ .setLayer(mTaskBackgroundSurface, -1)
+ .show(mTaskBackgroundSurface);
// Caption view
mCaptionWindowManager.setConfiguration(taskConfig);
@@ -301,6 +307,10 @@
mTaskBackgroundSurface.release();
mTaskBackgroundSurface = null;
}
+
+ final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
+ wct.removeInsetsProvider(mTaskInfo.token, CAPTION_INSETS_TYPES);
+ mTaskOrganizer.applyTransaction(wct);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 07e19be..4877442 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -229,7 +229,7 @@
?.layerStackSpace
?: error("Display not found")
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(displayBounds.width, displayBounds.width))
+ dividerBar.drag(Point(displayBounds.width * 4 / 5, displayBounds.height * 4 / 5))
}
fun doubleTapDividerToSwitch(device: UiDevice) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7517e8a..f865649 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -59,6 +59,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -85,10 +86,12 @@
@Mock
private CompatUIController mCompatUI;
@Mock
- private ShellInit mShellInit;
+ private ShellExecutor mTestExecutor;
+ @Mock
+ private ShellCommandHandler mShellCommandHandler;
- ShellTaskOrganizer mOrganizer;
- private final ShellExecutor mTestExecutor = mock(ShellExecutor.class);
+ private ShellTaskOrganizer mOrganizer;
+ private ShellInit mShellInit;
private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -132,8 +135,11 @@
doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
- mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mTaskOrganizerController,
- mCompatUI, Optional.empty(), Optional.empty(), mTestExecutor));
+ mShellInit = spy(new ShellInit(mTestExecutor));
+ mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
+ mTaskOrganizerController, mCompatUI, Optional.empty(), Optional.empty(),
+ mTestExecutor));
+ mShellInit.init();
}
@Test
@@ -142,9 +148,12 @@
}
@Test
- public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
- mOrganizer.registerOrganizer();
+ public void instantiate_addDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+ @Test
+ public void testInit_sendRegisterTaskOrganizer() throws RemoteException {
verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index ba81602..b0dd781 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -61,6 +62,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Ignore;
@@ -81,6 +83,7 @@
private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
+ private ShellInit mShellInit;
@Rule
public TestableContext mContext =
@@ -110,10 +113,12 @@
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION,
ANIMATION_ENABLED);
mTestableLooper = TestableLooper.get(this);
- mController = new BackAnimationController(
+ mShellInit = spy(new ShellInit(mShellExecutor));
+ mController = new BackAnimationController(mShellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
+ mShellInit.init();
mEventTime = 0;
mShellExecutor.flushAll();
}
@@ -160,6 +165,11 @@
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
@Ignore("b/207481538")
public void crossActivity_screenshotAttachedAndVisible() {
SurfaceControl screenshotSurface = new SurfaceControl();
@@ -233,10 +243,12 @@
public void animationDisabledFromSettings() throws RemoteException {
// Toggle the setting off
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
- mController = new BackAnimationController(
+ ShellInit shellInit = new ShellInit(mShellExecutor);
+ mController = new BackAnimationController(shellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
+ shellInit.init();
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
RemoteAnimationTarget animationTarget = createAnimationTarget();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 828c13e..6292130 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -54,6 +55,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -79,6 +81,7 @@
private static final int TASK_ID = 12;
private CompatUIController mController;
+ private ShellInit mShellInit;
private @Mock ShellController mMockShellController;
private @Mock DisplayController mMockDisplayController;
private @Mock DisplayInsetsController mMockDisplayInsetsController;
@@ -107,9 +110,10 @@
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
- mController = new CompatUIController(mContext, mMockShellController, mMockDisplayController,
- mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor,
- mMockTransitionsLazy) {
+ mShellInit = spy(new ShellInit(mMockExecutor));
+ mController = new CompatUIController(mContext, mShellInit, mMockShellController,
+ mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
+ mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -122,10 +126,16 @@
return mMockLetterboxEduLayout;
}
};
+ mShellInit.init();
spyOn(mController);
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
public void instantiateController_registerKeyguardChangeListener() {
verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
index dcc504a..6c301bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
@@ -17,7 +17,9 @@
package com.android.wm.shell.hidedisplaycutout;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +31,10 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -45,17 +50,32 @@
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Mock
+ private ShellCommandHandler mShellCommandHandler;
+ @Mock
private ShellController mShellController;
@Mock
private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
private HideDisplayCutoutController mHideDisplayCutoutController;
+ private ShellInit mShellInit;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mHideDisplayCutoutController = new HideDisplayCutoutController(
- mContext, mShellController, mMockDisplayAreaOrganizer);
+ mShellInit = spy(new ShellInit(mock(ShellExecutor.class)));
+ mHideDisplayCutoutController = new HideDisplayCutoutController(mContext, mShellInit,
+ mShellCommandHandler, mShellController, mMockDisplayAreaOrganizer);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index a919ad0..ecfb427 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -49,6 +49,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -74,6 +75,7 @@
@Mock private WindowContainerTransaction mTransaction;
@Mock private KidsModeSettingsObserver mObserver;
@Mock private ShellInit mShellInit;
+ @Mock private ShellCommandHandler mShellCommandHandler;
@Mock private DisplayInsetsController mDisplayInsetsController;
KidsModeTaskOrganizer mOrganizer;
@@ -87,14 +89,20 @@
} catch (RemoteException e) {
}
// NOTE: KidsModeTaskOrganizer should have a null CompatUIController.
- mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mTaskOrganizerController,
- mSyncTransactionQueue, mDisplayController, mDisplayInsetsController,
- Optional.empty(), Optional.empty(), mObserver, mTestExecutor, mHandler));
+ mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mShellInit, mShellCommandHandler,
+ mTaskOrganizerController, mSyncTransactionQueue, mDisplayController,
+ mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver,
+ mTestExecutor, mHandler));
doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
public void testKidsModeOn() {
doReturn(true).when(mObserver).isEnabled();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index dbf93ae..90645ce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -36,7 +36,6 @@
import android.graphics.Rect;
import android.os.Handler;
-import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
@@ -49,7 +48,9 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -61,18 +62,20 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedControllerTest extends OneHandedTestCase {
- private int mCurrentUser = UserHandle.myUserId();
Display mDisplay;
OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
OneHandedController mSpiedOneHandedController;
OneHandedTimeoutHandler mSpiedTimeoutHandler;
OneHandedState mSpiedTransitionState;
+ ShellInit mShellInit;
@Mock
+ ShellCommandHandler mMockShellCommandHandler;
+ @Mock
ShellController mMockShellController;
@Mock
- DisplayLayout mDisplayLayout;
+ DisplayLayout mMockDisplayLayout;
@Mock
DisplayController mMockDisplayController;
@Mock
@@ -102,7 +105,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
- mDisplayLayout = Mockito.mock(DisplayLayout.class);
+ mMockDisplayLayout = Mockito.mock(DisplayLayout.class);
mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
mSpiedTransitionState = spy(new OneHandedState());
@@ -122,11 +125,14 @@
when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
new Rect(0, 0, 1080, 2400));
- when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mMockDisplayLayout);
+ mShellInit = spy(new ShellInit(mMockShellMainExecutor));
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mShellInit,
+ mMockShellCommandHandler,
mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
@@ -141,6 +147,17 @@
mMockShellMainExecutor,
mMockShellMainHandler)
);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
}
@Test
@@ -308,9 +325,9 @@
@Test
public void testRotation90CanNotStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
mSpiedTransitionState.setState(STATE_NONE);
- when(mDisplayLayout.isLandscape()).thenReturn(true);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(true);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
@@ -320,10 +337,10 @@
@Test
public void testRotation180CanStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
mSpiedTransitionState.setState(STATE_NONE);
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
- when(mDisplayLayout.isLandscape()).thenReturn(false);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(false);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
@@ -333,9 +350,9 @@
@Test
public void testRotation270CanNotStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
mSpiedTransitionState.setState(STATE_NONE);
- when(mDisplayLayout.isLandscape()).thenReturn(true);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(true);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index e6a8220..a39bdf0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -41,7 +41,9 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -61,6 +63,10 @@
OneHandedState mSpiedState;
@Mock
+ ShellInit mMockShellInit;
+ @Mock
+ ShellCommandHandler mMockShellCommandHandler;
+ @Mock
ShellController mMockShellController;
@Mock
DisplayController mMockDisplayController;
@@ -111,6 +117,8 @@
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mMockShellInit,
+ mMockShellCommandHandler,
mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 857f578..579638d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -21,13 +21,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
@@ -70,7 +70,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PipTaskOrganizerTest extends ShellTestCase {
- private PipTaskOrganizer mSpiedPipTaskOrganizer;
+ private PipTaskOrganizer mPipTaskOrganizer;
@Mock private DisplayController mMockDisplayController;
@Mock private SyncTransactionQueue mMockSyncTransactionQueue;
@@ -100,14 +100,15 @@
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor();
- mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor);
mMainExecutor.flushAll();
preparePipTaskOrg();
+ preparePipSurfaceTransactionHelper();
}
@Test
@@ -124,14 +125,14 @@
public void startSwipePipToHome_updatesAspectRatio() {
final Rational aspectRatio = new Rational(2, 1);
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
}
@Test
public void startSwipePipToHome_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
}
@@ -140,7 +141,7 @@
public void startSwipePipToHome_updatesOverrideMinSize() {
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
createPipParams(null));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -150,7 +151,7 @@
public void onTaskAppeared_updatesAspectRatio() {
final Rational aspectRatio = new Rational(2, 1);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(aspectRatio)), mock(SurfaceControl.class));
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
@@ -158,7 +159,7 @@
@Test
public void onTaskAppeared_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
mock(SurfaceControl.class));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
@@ -168,7 +169,7 @@
public void onTaskAppeared_updatesOverrideMinSize() {
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.onTaskAppeared(
+ mPipTaskOrganizer.onTaskAppeared(
createTaskInfo(mComponent1, createPipParams(null), minSize),
mock(SurfaceControl.class));
@@ -179,16 +180,16 @@
public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), mock(SurfaceControl.class));
// It is in entering transition, should defer onTaskInfoChanged callback in this case.
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
createPipParams(newAspectRatio)));
verify(mMockPipParamsChangedForwarder, never()).notifyAspectRatioChanged(anyFloat());
// Once the entering transition finishes, the new aspect ratio applies in a deferred manner
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
verify(mMockPipParamsChangedForwarder)
.notifyAspectRatioChanged(newAspectRatio.floatValue());
}
@@ -197,11 +198,11 @@
public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
createPipParams(newAspectRatio)));
verify(mMockPipParamsChangedForwarder)
@@ -210,11 +211,11 @@
@Test
public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null)));
assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName());
@@ -222,12 +223,12 @@
@Test
public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null), minSize));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -235,23 +236,42 @@
@Test
public void onTaskVanished_clearsPipBounds() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
mPipBoundsState.setBounds(new Rect(100, 100, 200, 150));
- mSpiedPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
+ mPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
assertTrue(mPipBoundsState.getBounds().isEmpty());
}
+ private void sendOnPipTransitionFinished(
+ @PipAnimationController.TransitionDirection int direction) {
+ mPipTaskOrganizer.sendOnPipTransitionFinished(direction);
+ // PipTransitionController will call back into PipTaskOrganizer.
+ mPipTaskOrganizer.mPipTransitionCallback.onPipTransitionFinished(direction);
+ }
+
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
mContext.getResources(), true, true));
- mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
- mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(
+ mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+ mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
- doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
- doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
+ }
+
+ private void preparePipSurfaceTransactionHelper() {
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .crop(any(), any(), any());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .resetScale(any(), any(), any());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .round(any(), any(), anyBoolean());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .scale(any(), any(), any(), any(), anyFloat());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .alpha(any(), any(), anyFloat());
+ doNothing().when(mMockPipSurfaceTransactionHelper).onDensityOrFontScaleChanged(any());
}
private static ActivityManager.RunningTaskInfo createTaskInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index f192514..9ed8d84 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,7 +55,9 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -74,7 +76,9 @@
@TestableLooper.RunWithLooper
public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
+ private ShellInit mShellInit;
+ @Mock private ShellCommandHandler mMockShellCommandHandler;
@Mock private ShellController mMockShellController;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@@ -105,19 +109,31 @@
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mMockExecutor).execute(any());
- mPipController = new PipController(mContext, mMockShellController, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipKeepClearAlgorithm,
+ mShellInit = spy(new ShellInit(mMockExecutor));
+ mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
+ mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mPipParamsChangedForwarder,
mMockOneHandedController, mMockExecutor);
+ mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
}
@Test
+ public void instantiatePipController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+
+ @Test
public void instantiatePipController_registerConfigChangeListener() {
verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
}
@@ -149,9 +165,10 @@
when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
- assertNull(PipController.create(spyContext, mMockShellController, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipKeepClearAlgorithm,
+ ShellInit shellInit = new ShellInit(mMockExecutor);
+ assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
+ mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -217,7 +234,7 @@
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper).movePip(any(Rect.class));
+ verify(mMockPipTaskOrganizer).scheduleFinishResizePip(any(Rect.class));
}
@Test
@@ -233,7 +250,7 @@
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper, never()).movePip(any(Rect.class));
+ verify(mMockPipTaskOrganizer, never()).scheduleFinishResizePip(any(Rect.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e4722..cc51efd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -179,6 +179,16 @@
}
@Test
+ fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+ triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+ assertMovement(STASHED_BOUNDS)
+ assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+ triggerImmediatePlacement(STASHED_PLACEMENT)
+ assertNoMovementUpTo(time + FAR_FUTURE)
+ }
+
+ @Test
fun testInMoveMode_KeepAtAnchor() {
startMoveMode()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index d406a4e..81bb609 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -48,6 +50,7 @@
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -73,21 +76,35 @@
@Mock
private TaskStackListenerImpl mTaskStackListener;
@Mock
- private ShellInit mShellInit;
+ private ShellCommandHandler mShellCommandHandler;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
+ private ShellInit mShellInit;
private ShellExecutor mMainExecutor;
@Before
public void setUp() {
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+ mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mTaskStackListener, mMainExecutor));
- mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit,
+ mShellCommandHandler, mTaskStackListener, mMainExecutor));
+ mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), isA(RecentTasksController.class));
+ }
+
+ @Test
+ public void instantiateController_addDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(),
+ isA(RecentTasksController.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 10788f9..5a68361 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -20,12 +20,14 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -53,6 +55,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -72,8 +75,9 @@
@RunWith(AndroidJUnit4.class)
public class SplitScreenControllerTests extends ShellTestCase {
- @Mock ShellController mShellController;
@Mock ShellInit mShellInit;
+ @Mock ShellController mShellController;
+ @Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock SyncTransactionQueue mSyncQueue;
@Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@@ -93,9 +97,10 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
- mShellController, mTaskOrganizer, mSyncQueue, mRootTDAOrganizer, mDisplayController,
- mDisplayImeController, mDisplayInsetsController, mDragAndDropController,
- mTransitions, mTransactionPool, mIconProvider, mRecentTasks, mMainExecutor));
+ mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+ mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+ mIconProvider, mRecentTasks, mMainExecutor));
}
@Test
@@ -104,14 +109,31 @@
}
@Test
+ public void instantiateController_registerDumpCallback() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onInit();
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerCommandCallback() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onInit();
+ verify(mShellCommandHandler, times(1)).addCommandCallback(eq("splitscreen"), any(), any());
+ }
+
+ @Test
public void testControllerRegistersKeyguardChangeListener() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
mSplitScreenController.onInit();
verify(mShellController, times(1)).addKeyguardChangeListener(any());
}
@Test
- public void testIsLaunchingAdjacently_notInSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
@@ -120,7 +142,7 @@
ActivityManager.RunningTaskInfo focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
// Verify launching different activity returns false.
@@ -128,28 +150,40 @@
focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
@Test
- public void testIsLaunchingAdjacently_inSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_inSplitScreen() {
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
-
- // Verify launching the same activity returns true.
Intent startIntent = createStartIntent("startActivity");
- ActivityManager.RunningTaskInfo pairingTaskInfo =
+ ActivityManager.RunningTaskInfo sameTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ Intent diffIntent = createStartIntent("diffActivity");
+ ActivityManager.RunningTaskInfo differentTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
+
+ // Verify launching the same activity return false.
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
- // Verify launching different activity returns false.
- Intent diffIntent = createStartIntent("diffActivity");
- pairingTaskInfo =
- createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ // Verify launching the same activity as adjacent returns true.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching different activity from adjacent returns false.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 02311ba..39e58ff 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -45,6 +45,8 @@
@Mock
private ShellInit mShellInit;
@Mock
+ private ShellCommandHandler mShellCommandHandler;
+ @Mock
private ShellExecutor mExecutor;
private ShellController mController;
@@ -56,7 +58,7 @@
MockitoAnnotations.initMocks(this);
mKeyguardChangeListener = new TestKeyguardChangeListener();
mConfigChangeListener = new TestConfigurationChangeListener();
- mController = new ShellController(mShellInit, mExecutor);
+ mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
mController.onConfigurationChanged(getConfigurationCopy());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index b318bb2..226843e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -204,6 +204,8 @@
verify(mMockSurfaceControlStartT)
.setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
+ verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+ verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
verify(mMockSurfaceControlViewHostFactory)
.create(any(), eq(defaultDisplay), any(), anyBoolean());
@@ -233,6 +235,57 @@
}
@Test
+ public void testLayoutResultCalculation_visibleFocusedTaskToInvisible() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+ // 64px.
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mOutsetsDp.set(10, 20, 30, 40);
+
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHost, never()).release();
+ verify(decorContainerSurface, never()).release();
+ verify(taskBackgroundSurface, never()).release();
+ verify(mMockWindowContainerTransaction, never())
+ .removeInsetsProvider(eq(taskInfo.token), any());
+
+ taskInfo.isVisible = false;
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHost).release();
+ verify(decorContainerSurface).release();
+ verify(taskBackgroundSurface).release();
+ verify(mMockWindowContainerTransaction).removeInsetsProvider(eq(taskInfo.token), any());
+ }
+
+ @Test
public void testNotCrashWhenDisplayAppearsAfterTask() {
doReturn(mock(Display.class)).when(mMockDisplayController)
.getDisplay(Display.DEFAULT_DISPLAY);
@@ -282,7 +335,7 @@
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
- mMockSurfaceControlViewHostFactory);
+ () -> mMockWindowContainerTransaction, mMockSurfaceControlViewHostFactory);
}
private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
@@ -313,9 +366,11 @@
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
+ surfaceControlBuilderSupplier, windowContainerTransactionSupplier,
+ surfaceControlViewHostFactory);
}
@Override
diff --git a/libs/hwui/CopyRequest.h b/libs/hwui/CopyRequest.h
new file mode 100644
index 0000000..5fbd5f9
--- /dev/null
+++ b/libs/hwui/CopyRequest.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "Rect.h"
+#include "hwui/Bitmap.h"
+
+namespace android::uirenderer {
+
+// Keep in sync with PixelCopy.java codes
+enum class CopyResult {
+ Success = 0,
+ UnknownError = 1,
+ Timeout = 2,
+ SourceEmpty = 3,
+ SourceInvalid = 4,
+ DestinationInvalid = 5,
+};
+
+struct CopyRequest {
+ Rect srcRect;
+ CopyRequest(Rect srcRect) : srcRect(srcRect) {}
+ virtual ~CopyRequest() {}
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) = 0;
+ virtual void onCopyFinished(CopyResult result) = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 1191b92..02c2e67 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -49,8 +49,7 @@
#define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)
-CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
- SkBitmap* bitmap) {
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
ATRACE_CALL();
// Setup the source
AHardwareBuffer* rawSourceBuffer;
@@ -63,30 +62,33 @@
// Really this shouldn't ever happen, but better safe than sorry.
if (err == UNKNOWN_TRANSACTION) {
ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
- return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
windowTransform);
if (err != NO_ERROR) {
ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
if (rawSourceBuffer == nullptr) {
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
+ return request->onCopyFinished(CopyResult::SourceEmpty);
}
UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
AHardwareBuffer_Desc description;
AHardwareBuffer_describe(sourceBuffer.get(), &description);
if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
+ {
+ ATRACE_NAME("sync_wait");
+ if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return request->onCopyFinished(CopyResult::Timeout);
+ }
}
sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
@@ -95,12 +97,12 @@
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
if (!image.get()) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
sk_sp<GrDirectContext> grContext = mRenderThread.requireGrContext();
- SkRect srcRect = inSrcRect.toSkRect();
+ SkRect srcRect = request->srcRect.toSkRect();
SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
SkISize imageWH = SkISize::Make(description.width, description.height);
@@ -148,10 +150,12 @@
ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
SK_RECT_ARGS(textureRect));
if (!srcRect.intersect(textureRect)) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
+ SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
+ SkBitmap* bitmap = &skBitmap;
sk_sp<SkSurface> tmpSurface =
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
@@ -164,7 +168,7 @@
tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
@@ -235,52 +239,13 @@
!tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
!tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
ALOGW("Unable to convert content into the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
bitmap->notifyPixelsChanged();
- return CopyResult::Success;
-}
-
-CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
- SkBitmap* bitmap) {
- // Setup the source
- AHardwareBuffer* rawSourceBuffer;
- int rawSourceFence;
- Matrix4 texTransform;
- status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
- texTransform.data);
- base::unique_fd sourceFence(rawSourceFence);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (rawSourceBuffer == nullptr) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
-
- UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
- AHardwareBuffer_Desc description;
- AHardwareBuffer_describe(sourceBuffer.get(), &description);
- if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
-
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
- sk_sp<SkImage> image =
- SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
- return copyImageInto(image, srcRect, bitmap);
+ return request->onCopyFinished(CopyResult::Success);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -318,14 +283,14 @@
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
SkBitmap* bitmap) {
ATRACE_CALL();
+ if (!image.get()) {
+ return CopyResult::UnknownError;
+ }
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
} else {
mRenderThread.requireVkContext();
}
- if (!image.get()) {
- return CopyResult::UnknownError;
- }
int imgWidth = image->width();
int imgHeight = image->height();
sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index aa6e43c..a092d47 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,12 +16,13 @@
#pragma once
+#include <SkRefCnt.h>
+
+#include "CopyRequest.h"
#include "Matrix.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
-#include <SkRefCnt.h>
-
class SkBitmap;
class SkImage;
struct SkRect;
@@ -35,23 +36,13 @@
class DeferredLayerUpdater;
class Layer;
-// Keep in sync with PixelCopy.java codes
-enum class CopyResult {
- Success = 0,
- UnknownError = 1,
- Timeout = 2,
- SourceEmpty = 3,
- SourceInvalid = 4,
- DestinationInvalid = 5,
-};
-
class Readback {
public:
explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
+ void copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request);
CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);
@@ -59,7 +50,6 @@
CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
private:
- CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 6325226..f5ed568 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -140,15 +140,13 @@
static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
Typeface* face = toTypeface(faceHandle);
- const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
- const size_t length = tagSet.size();
+ const size_t length = face->fFontCollection->getSupportedAxesCount();
if (length == 0) {
return nullptr;
}
std::vector<jint> tagVec(length);
- int index = 0;
- for (const auto& tag : tagSet) {
- tagVec[index++] = tag;
+ for (size_t i = 0; i < length; i++) {
+ tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
}
std::sort(tagVec.begin(), tagVec.end());
const jintArray result = env->NewIntArray(length);
@@ -382,18 +380,6 @@
env->SetStaticObjectField(cls, fid, typeface);
}
-// Critical Native
-static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
- return toTypeface(faceHandle)->fFontCollection->getFamilyCount();
-}
-
-// Critical Native
-static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
- std::shared_ptr<minikin::FontFamily> family =
- toTypeface(faceHandle)->fFontCollection->getFamilyAt(index);
- return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
-}
-
// Regular JNI
static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
ScopedUtfChars filePath(env, jFilePath);
@@ -433,8 +419,6 @@
{"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
{"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
(void*)Typeface_forceSetStaticFinalField},
- {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
- {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
{"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
{"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
{"nativeRegisterLocaleList", "(Ljava/lang/String;)V", (void*)Typeface_registerLocaleList},
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 87eda7e..1c5f126 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -8,6 +8,8 @@
#include "graphics_jni_helpers.h"
+#include <csetjmp>
+
YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
// Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
// for now.
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 55b1f23..4f281fc 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -88,6 +88,11 @@
jmethodID onFrameComplete;
} gFrameCompleteCallback;
+struct {
+ jmethodID onCopyFinished;
+ jmethodID getDestinationBitmap;
+} gCopyRequest;
+
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -672,15 +677,41 @@
}
}
-static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
- jobject clazz, jobject jsurface, jint left, jint top,
- jint right, jint bottom, jlong bitmapPtr) {
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+class CopyRequestAdapter : public CopyRequest {
+public:
+ CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+ : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ JNIEnv* env = getenv(mRefHolder.vm());
+ jlong bitmapPtr = env->CallLongMethod(
+ mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
+ SkBitmap bitmap;
+ bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ JNIEnv* env = getenv(mRefHolder.vm());
+ env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+ static_cast<jint>(result));
+ }
+
+private:
+ JGlobalRefHolder mRefHolder;
+};
+
+static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz,
+ jobject jsurface, jint left, jint top,
+ jint right, jint bottom,
+ jobject jCopyRequest) {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
+ Rect(left, top, right, bottom));
ANativeWindow* window = fromSurface(env, jsurface);
- jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+ RenderProxy::copySurfaceInto(window, std::move(copyRequest));
ANativeWindow_release(window);
- return result;
}
class ContextFactory : public IContextFactory {
@@ -969,7 +1000,8 @@
(void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
{"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver},
{"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver},
- {"nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I",
+ {"nCopySurfaceInto",
+ "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V",
(void*)android_view_ThreadedRenderer_copySurfaceInto},
{"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
(void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode},
@@ -1042,6 +1074,11 @@
gFrameCompleteCallback.onFrameComplete =
GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
+ jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest");
+ gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V");
+ gCopyRequest.getDestinationBitmap =
+ GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J");
+
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b2ba15c..40a0bac 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -364,12 +364,13 @@
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
- SkBitmap* bitmap) {
+void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
auto& thread = RenderThread::getInstance();
- return static_cast<int>(thread.queue().runSync([&]() -> auto {
- return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
- }));
+ ANativeWindow_acquire(window);
+ thread.queue().post([&thread, window, request = std::move(request)] {
+ thread.readback().copySurfaceInto(window, request);
+ ANativeWindow_release(window);
+ });
}
void RenderProxy::prepareToDraw(Bitmap& bitmap) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bbfeeac..2a99a73 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -19,13 +19,14 @@
#include <SkRefCnt.h>
#include <android/native_window.h>
-#include <cutils/compiler.h>
#include <android/surface_control.h>
+#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "../FrameMetricsObserver.h"
#include "../IContextFactory.h"
#include "ColorMode.h"
+#include "CopyRequest.h"
#include "DrawFrameTask.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
@@ -137,8 +138,7 @@
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
- int bottom, SkBitmap* bitmap);
+ static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
static void prepareToDraw(Bitmap& bitmap);
static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index 070a339..13a4381 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -25,17 +25,51 @@
class MagnifierAnimation;
+using Rect = android::uirenderer::Rect;
+
static TestScene::Registrar _Magnifier(TestScene::Info{
"magnifier", "A sample magnifier using Readback",
TestScene::simpleCreateScene<MagnifierAnimation>});
+class BlockingCopyRequest : public CopyRequest {
+ sk_sp<Bitmap> mDestination;
+ std::mutex mLock;
+ std::condition_variable mCondVar;
+ CopyResult mResult;
+
+public:
+ BlockingCopyRequest(::Rect rect, sk_sp<Bitmap> bitmap)
+ : CopyRequest(rect), mDestination(bitmap) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ SkBitmap bitmap;
+ mDestination->getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ std::unique_lock _lock{mLock};
+ mResult = result;
+ mCondVar.notify_all();
+ }
+
+ CopyResult waitForResult() {
+ std::unique_lock _lock{mLock};
+ mCondVar.wait(_lock);
+ return mResult;
+ }
+};
+
class MagnifierAnimation : public TestScene {
public:
sp<RenderNode> card;
sp<RenderNode> zoomImageView;
+ sk_sp<Bitmap> magnifier;
+ std::shared_ptr<BlockingCopyRequest> copyRequest;
void createContent(int width, int height, Canvas& canvas) override {
magnifier = TestUtils::createBitmap(200, 100);
+ setupCopyRequest();
SkBitmap temp;
magnifier->getSkBitmap(&temp);
temp.eraseColor(Color::White);
@@ -65,19 +99,20 @@
canvas.enableZ(false);
}
+ void setupCopyRequest() {
+ constexpr int x = 90;
+ constexpr int y = 325;
+ copyRequest = std::make_shared<BlockingCopyRequest>(
+ ::Rect(x, y, x + magnifier->width(), y + magnifier->height()), magnifier);
+ }
+
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
card->mutateStagingProperties().setTranslationX(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
if (renderTarget) {
- SkBitmap temp;
- magnifier->getSkBitmap(&temp);
- constexpr int x = 90;
- constexpr int y = 325;
- RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
- y + magnifier->height(), &temp);
+ RenderProxy::copySurfaceInto(renderTarget.get(), copyRequest);
+ copyRequest->waitForResult();
}
}
-
- sk_sp<Bitmap> magnifier;
};
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index c213fcc..60c812a 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -243,12 +243,23 @@
* Flag used when playback is restricted by AppOps manager with OP_PLAY_AUDIO.
*/
public static final int PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3);
+ /**
+ * @hide
+ * Flag used when muted by client volume.
+ */
+ public static final int PLAYER_MUTE_CLIENT_VOLUME = (1 << 4);
+ /**
+ * @hide
+ * Flag used when muted by volume shaper.
+ */
+ public static final int PLAYER_MUTE_VOLUME_SHAPER = (1 << 5);
/** @hide */
@IntDef(
flag = true,
value = {PLAYER_MUTE_MASTER, PLAYER_MUTE_STREAM_VOLUME, PLAYER_MUTE_STREAM_MUTED,
- PLAYER_MUTE_PLAYBACK_RESTRICTED})
+ PLAYER_MUTE_PLAYBACK_RESTRICTED, PLAYER_MUTE_CLIENT_VOLUME,
+ PLAYER_MUTE_VOLUME_SHAPER})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerMuteEvent {
}
@@ -678,7 +689,9 @@
+ " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
+ " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
+ " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
- != 0);
+ != 0)
+ + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
+ + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
}
//=====================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 955bfcc..444366a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,6 +65,8 @@
private static final String TAG = "AudioSystem";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
// private constructor to prevent instantiating AudioSystem
private AudioSystem() {
throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -243,6 +245,8 @@
public static final int AUDIO_FORMAT_LDAC = 0x23000000;
/** @hide */
public static final int AUDIO_FORMAT_LC3 = 0x2B000000;
+ /** @hide */
+ public static final int AUDIO_FORMAT_OPUS = 0x08000000;
/** @hide */
@@ -254,7 +258,9 @@
AUDIO_FORMAT_APTX,
AUDIO_FORMAT_APTX_HD,
AUDIO_FORMAT_LDAC,
- AUDIO_FORMAT_LC3}
+ AUDIO_FORMAT_LC3,
+ AUDIO_FORMAT_OPUS
+ }
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtCodec {}
@@ -287,6 +293,7 @@
case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
+ case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT codec");
@@ -329,6 +336,8 @@
return AudioSystem.AUDIO_FORMAT_LDAC;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
return AudioSystem.AUDIO_FORMAT_LC3;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return AudioSystem.AUDIO_FORMAT_OPUS;
default:
Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
+ " for conversion to audio format");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 472586b..fde7afd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,7 +18,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@
import android.hardware.HardwareBuffer.Usage;
import android.hardware.SyncFence;
import android.hardware.camera2.MultiResolutionImageReader;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -87,6 +92,38 @@
/**
* <p>
+ * Flag to gate correct exception thrown by {@code #detachImage}.
+ * </p>
+ * <p>
+ * {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
+ * the event of an error; a native helper method to this threw
+ * {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
+ * {@code Image}.
+ * <p>
+ * This previously undocumented exception behavior continues through Android T.
+ * </p>
+ * <p>
+ * After Android T, the native helper method only throws {@code IllegalStateExceptions} in
+ * accordance with the documentation.
+ * </p>
+ * <p>
+ * {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
+ * the image. Behavior on apps targeting API levels <= T remains unchanged.
+ * </p>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
+
+ /**
+ * Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
+ * detaching image.
+ */
+ private final boolean mDetachThrowsIseOnly =
+ CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
+
+ /**
+ * <p>
* Create a new reader for images of the desired size and format.
* </p>
* <p>
@@ -825,10 +862,10 @@
* </p>
* <p>
* After this call, the ImageReader no longer owns this image, and the image
- * ownership can be transfered to another entity like {@link ImageWriter}
+ * ownership can be transferred to another entity like {@link ImageWriter}
* via {@link ImageWriter#queueInputImage}. It's up to the new owner to
* release the resources held by this image. For example, if the ownership
- * of this image is transfered to an {@link ImageWriter}, the image will be
+ * of this image is transferred to an {@link ImageWriter}, the image will be
* freed by the ImageWriter after the image data consumption is done.
* </p>
* <p>
@@ -849,16 +886,22 @@
* @throws IllegalStateException If the ImageReader or image have been
* closed, or the has been detached, or has not yet been
* acquired.
+ * @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
+ * {@code RuntimeException} is only thrown for applications targeting SDK <=
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * For applications targeting SDK >
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU},
+ * this method only throws {@code IllegalStateException}.
* @hide
*/
- public void detachImage(Image image) {
- if (image == null) {
+ public void detachImage(@Nullable Image image) {
+ if (image == null) {
throw new IllegalArgumentException("input image must not be null");
- }
- if (!isImageOwnedbyMe(image)) {
+ }
+ if (!isImageOwnedbyMe(image)) {
throw new IllegalArgumentException("Trying to detach an image that is not owned by"
+ " this ImageReader");
- }
+ }
SurfaceImage si = (SurfaceImage) image;
si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@
throw new IllegalStateException("Image was already detached from this ImageReader");
}
- nativeDetachImage(image);
+ nativeDetachImage(image, mDetachThrowsIseOnly);
si.clearSurfacePlanes();
si.mPlanes = null;
si.setDetached(true);
@@ -1408,7 +1451,7 @@
private synchronized native void nativeClose();
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
- private synchronized native int nativeDetachImage(Image i);
+ private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
private synchronized native void nativeDiscardFreeBuffers();
/**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7b27358..53d4efa 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,6 +45,7 @@
import java.nio.ReadOnlyBufferException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -1811,7 +1812,7 @@
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- validateInputByteBuffer(mCachedInputBuffers, index);
+ validateInputByteBufferLocked(mCachedInputBuffers, index);
break;
case BUFFER_MODE_BLOCK:
while (mQueueRequests.size() <= index) {
@@ -1840,7 +1841,7 @@
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+ validateOutputByteBufferLocked(mCachedOutputBuffers, index, info);
break;
case BUFFER_MODE_BLOCK:
while (mOutputFrames.size() <= index) {
@@ -2328,10 +2329,6 @@
*/
public final void start() {
native_start();
- synchronized(mBufferLock) {
- cacheBuffers(true /* input */);
- cacheBuffers(false /* input */);
- }
}
private native final void native_start();
@@ -2388,8 +2385,10 @@
*/
public final void flush() {
synchronized(mBufferLock) {
- invalidateByteBuffers(mCachedInputBuffers);
- invalidateByteBuffers(mCachedOutputBuffers);
+ invalidateByteBuffersLocked(mCachedInputBuffers);
+ invalidateByteBuffersLocked(mCachedOutputBuffers);
+ mValidInputIndices.clear();
+ mValidOutputIndices.clear();
mDequeuedInputBuffers.clear();
mDequeuedOutputBuffers.clear();
}
@@ -2673,14 +2672,14 @@
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
}
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.remove(index);
}
try {
native_queueInputBuffer(
index, offset, size, presentationTimeUs, flags);
} catch (CryptoException | IllegalStateException e) {
- revalidateByteBuffer(mCachedInputBuffers, index);
+ revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
throw e;
}
}
@@ -2943,14 +2942,14 @@
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
}
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.remove(index);
}
try {
native_queueSecureInputBuffer(
index, offset, info, presentationTimeUs, flags);
} catch (CryptoException | IllegalStateException e) {
- revalidateByteBuffer(mCachedInputBuffers, index);
+ revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
throw e;
}
}
@@ -2984,7 +2983,7 @@
int res = native_dequeueInputBuffer(timeoutUs);
if (res >= 0) {
synchronized(mBufferLock) {
- validateInputByteBuffer(mCachedInputBuffers, res);
+ validateInputByteBufferLocked(mCachedInputBuffers, res);
}
}
return res;
@@ -3581,10 +3580,10 @@
int res = native_dequeueOutputBuffer(info, timeoutUs);
synchronized (mBufferLock) {
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- cacheBuffers(false /* input */);
+ cacheBuffersLocked(false /* input */);
} else if (res >= 0) {
- validateOutputByteBuffer(mCachedOutputBuffers, res, info);
- if (mHasSurface) {
+ validateOutputByteBufferLocked(mCachedOutputBuffers, res, info);
+ if (mHasSurface || mCachedOutputBuffers == null) {
mDequeuedOutputInfos.put(res, info.dup());
}
}
@@ -3678,9 +3677,9 @@
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.remove(index);
- if (mHasSurface) {
+ if (mHasSurface || mCachedOutputBuffers == null) {
info = mDequeuedOutputInfos.remove(index);
}
break;
@@ -3832,15 +3831,24 @@
private ByteBuffer[] mCachedInputBuffers;
private ByteBuffer[] mCachedOutputBuffers;
+ private BitSet mValidInputIndices = new BitSet();
+ private BitSet mValidOutputIndices = new BitSet();
+
private final BufferMap mDequeuedInputBuffers = new BufferMap();
private final BufferMap mDequeuedOutputBuffers = new BufferMap();
private final Map<Integer, BufferInfo> mDequeuedOutputInfos =
new HashMap<Integer, BufferInfo>();
final private Object mBufferLock;
- private final void invalidateByteBuffer(
- @Nullable ByteBuffer[] buffers, int index) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ private void invalidateByteBufferLocked(
+ @Nullable ByteBuffer[] buffers, int index, boolean input) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ indices.clear(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(false);
@@ -3848,9 +3856,14 @@
}
}
- private final void validateInputByteBuffer(
+ private void validateInputByteBufferLocked(
@Nullable ByteBuffer[] buffers, int index) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ mValidInputIndices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3859,10 +3872,16 @@
}
}
- private final void revalidateByteBuffer(
- @Nullable ByteBuffer[] buffers, int index) {
+ private void revalidateByteBuffer(
+ @Nullable ByteBuffer[] buffers, int index, boolean input) {
synchronized(mBufferLock) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ indices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3871,9 +3890,14 @@
}
}
- private final void validateOutputByteBuffer(
+ private void validateOutputByteBufferLocked(
@Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ mValidOutputIndices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3882,7 +3906,7 @@
}
}
- private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) {
+ private void invalidateByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
if (buffer != null) {
@@ -3892,27 +3916,29 @@
}
}
- private final void freeByteBuffer(@Nullable ByteBuffer buffer) {
+ private void freeByteBufferLocked(@Nullable ByteBuffer buffer) {
if (buffer != null /* && buffer.isDirect() */) {
// all of our ByteBuffers are direct
java.nio.NioUtils.freeDirectBuffer(buffer);
}
}
- private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) {
+ private void freeByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
- freeByteBuffer(buffer);
+ freeByteBufferLocked(buffer);
}
}
}
- private final void freeAllTrackedBuffers() {
+ private void freeAllTrackedBuffers() {
synchronized(mBufferLock) {
- freeByteBuffers(mCachedInputBuffers);
- freeByteBuffers(mCachedOutputBuffers);
+ freeByteBuffersLocked(mCachedInputBuffers);
+ freeByteBuffersLocked(mCachedOutputBuffers);
mCachedInputBuffers = null;
mCachedOutputBuffers = null;
+ mValidInputIndices.clear();
+ mValidOutputIndices.clear();
mDequeuedInputBuffers.clear();
mDequeuedOutputBuffers.clear();
mQueueRequests.clear();
@@ -3920,14 +3946,31 @@
}
}
- private final void cacheBuffers(boolean input) {
+ private void cacheBuffersLocked(boolean input) {
ByteBuffer[] buffers = null;
try {
buffers = getBuffers(input);
- invalidateByteBuffers(buffers);
+ invalidateByteBuffersLocked(buffers);
} catch (IllegalStateException e) {
// we don't get buffers in async mode
}
+ if (buffers != null) {
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ for (int i = 0; i < buffers.length; ++i) {
+ ByteBuffer buffer = buffers[i];
+ if (buffer == null || !indices.get(i)) {
+ continue;
+ }
+ buffer.setAccessible(true);
+ if (!input) {
+ BufferInfo info = mDequeuedOutputInfos.get(i);
+ if (info != null) {
+ buffer.limit(info.offset + info.size).position(info.offset);
+ }
+ }
+ }
+ indices.clear();
+ }
if (input) {
mCachedInputBuffers = buffers;
} else {
@@ -3963,6 +4006,9 @@
+ "objects and attach to QueueRequest objects.");
}
if (mCachedInputBuffers == null) {
+ cacheBuffersLocked(true /* input */);
+ }
+ if (mCachedInputBuffers == null) {
throw new IllegalStateException();
}
// FIXME: check codec status
@@ -4001,6 +4047,9 @@
+ "Please use getOutputFrame to get output frames.");
}
if (mCachedOutputBuffers == null) {
+ cacheBuffersLocked(false /* input */);
+ }
+ if (mCachedOutputBuffers == null) {
throw new IllegalStateException();
}
// FIXME: check codec status
@@ -4038,7 +4087,7 @@
}
ByteBuffer newBuffer = getBuffer(true /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.put(index, newBuffer);
}
return newBuffer;
@@ -4075,7 +4124,7 @@
}
Image newImage = getImage(true /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.put(index, newImage);
}
return newImage;
@@ -4111,7 +4160,7 @@
}
ByteBuffer newBuffer = getBuffer(false /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.put(index, newBuffer);
}
return newBuffer;
@@ -4146,7 +4195,7 @@
}
Image newImage = getImage(false /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.put(index, newImage);
}
return newImage;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 75fd64a..bf30c50 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1445,7 +1445,7 @@
sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
maxChannels = 255;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
- sampleRateRange = Range.create(1, 96000);
+ sampleRateRange = Range.create(1, 192000);
bitRates = Range.create(1, 10000000);
maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
new file mode 100644
index 0000000..9f92887
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.TvInteractiveAppService.Session;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.Surface;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+import java.util.List;
+
+/**
+ * Implements the internal ITvInteractiveAppSession interface.
+ * @hide
+ */
+public class ITvInteractiveAppSessionWrapper
+ extends ITvInteractiveAppSession.Stub implements HandlerCaller.Callback {
+ private static final String TAG = "ITvInteractiveAppSessionWrapper";
+
+ private static final int EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS = 1000;
+ private static final int EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS = 5 * 1000;
+
+ private static final int DO_RELEASE = 1;
+ private static final int DO_START_INTERACTIVE_APP = 2;
+ private static final int DO_STOP_INTERACTIVE_APP = 3;
+ private static final int DO_RESET_INTERACTIVE_APP = 4;
+ private static final int DO_CREATE_BI_INTERACTIVE_APP = 5;
+ private static final int DO_DESTROY_BI_INTERACTIVE_APP = 6;
+ private static final int DO_SET_TELETEXT_APP_ENABLED = 7;
+ private static final int DO_SEND_CURRENT_CHANNEL_URI = 8;
+ private static final int DO_SEND_CURRENT_CHANNEL_LCN = 9;
+ private static final int DO_SEND_STREAM_VOLUME = 10;
+ private static final int DO_SEND_TRACK_INFO_LIST = 11;
+ private static final int DO_SEND_CURRENT_TV_INPUT_ID = 12;
+ private static final int DO_SEND_SIGNING_RESULT = 13;
+ private static final int DO_NOTIFY_ERROR = 14;
+ private static final int DO_NOTIFY_TUNED = 15;
+ private static final int DO_NOTIFY_TRACK_SELECTED = 16;
+ private static final int DO_NOTIFY_TRACKS_CHANGED = 17;
+ private static final int DO_NOTIFY_VIDEO_AVAILABLE = 18;
+ private static final int DO_NOTIFY_VIDEO_UNAVAILABLE = 19;
+ private static final int DO_NOTIFY_CONTENT_ALLOWED = 20;
+ private static final int DO_NOTIFY_CONTENT_BLOCKED = 21;
+ private static final int DO_NOTIFY_SIGNAL_STRENGTH = 22;
+ private static final int DO_SET_SURFACE = 23;
+ private static final int DO_DISPATCH_SURFACE_CHANGED = 24;
+ private static final int DO_NOTIFY_BROADCAST_INFO_RESPONSE = 25;
+ private static final int DO_NOTIFY_AD_RESPONSE = 26;
+ private static final int DO_CREATE_MEDIA_VIEW = 27;
+ private static final int DO_RELAYOUT_MEDIA_VIEW = 28;
+ private static final int DO_REMOVE_MEDIA_VIEW = 29;
+
+ private final HandlerCaller mCaller;
+ private Session mSessionImpl;
+ private InputChannel mChannel;
+ private TvInteractiveAppEventReceiver mReceiver;
+
+ public ITvInteractiveAppSessionWrapper(
+ Context context, Session mSessionImpl, InputChannel channel) {
+ this.mSessionImpl = mSessionImpl;
+ mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+ mChannel = channel;
+ if (channel != null) {
+ mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
+ }
+ }
+
+ @Override
+ public void executeMessage(Message msg) {
+ if (mSessionImpl == null) {
+ return;
+ }
+
+ long startTime = System.nanoTime();
+ switch (msg.what) {
+ case DO_RELEASE: {
+ mSessionImpl.release();
+ mSessionImpl = null;
+ if (mReceiver != null) {
+ mReceiver.dispose();
+ mReceiver = null;
+ }
+ if (mChannel != null) {
+ mChannel.dispose();
+ mChannel = null;
+ }
+ break;
+ }
+ case DO_START_INTERACTIVE_APP: {
+ mSessionImpl.startInteractiveApp();
+ break;
+ }
+ case DO_STOP_INTERACTIVE_APP: {
+ mSessionImpl.stopInteractiveApp();
+ break;
+ }
+ case DO_RESET_INTERACTIVE_APP: {
+ mSessionImpl.resetInteractiveApp();
+ break;
+ }
+ case DO_CREATE_BI_INTERACTIVE_APP: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.createBiInteractiveApp((Uri) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_DESTROY_BI_INTERACTIVE_APP: {
+ mSessionImpl.destroyBiInteractiveApp((String) msg.obj);
+ break;
+ }
+ case DO_SET_TELETEXT_APP_ENABLED: {
+ mSessionImpl.setTeletextAppEnabled((Boolean) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_CHANNEL_URI: {
+ mSessionImpl.sendCurrentChannelUri((Uri) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_CHANNEL_LCN: {
+ mSessionImpl.sendCurrentChannelLcn((Integer) msg.obj);
+ break;
+ }
+ case DO_SEND_STREAM_VOLUME: {
+ mSessionImpl.sendStreamVolume((Float) msg.obj);
+ break;
+ }
+ case DO_SEND_TRACK_INFO_LIST: {
+ mSessionImpl.sendTrackInfoList((List<TvTrackInfo>) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_TV_INPUT_ID: {
+ mSessionImpl.sendCurrentTvInputId((String) msg.obj);
+ break;
+ }
+ case DO_SEND_SIGNING_RESULT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.sendSigningResult((String) args.arg1, (byte[]) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_ERROR: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyError((String) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_TUNED: {
+ mSessionImpl.notifyTuned((Uri) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_TRACK_SELECTED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyTrackSelected((Integer) args.arg1, (String) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_TRACKS_CHANGED: {
+ mSessionImpl.notifyTracksChanged((List<TvTrackInfo>) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_VIDEO_AVAILABLE: {
+ mSessionImpl.notifyVideoAvailable();
+ break;
+ }
+ case DO_NOTIFY_VIDEO_UNAVAILABLE: {
+ mSessionImpl.notifyVideoUnavailable((Integer) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_CONTENT_ALLOWED: {
+ mSessionImpl.notifyContentAllowed();
+ break;
+ }
+ case DO_NOTIFY_CONTENT_BLOCKED: {
+ mSessionImpl.notifyContentBlocked((TvContentRating) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_SIGNAL_STRENGTH: {
+ mSessionImpl.notifySignalStrength((Integer) msg.obj);
+ break;
+ }
+ case DO_SET_SURFACE: {
+ mSessionImpl.setSurface((Surface) msg.obj);
+ break;
+ }
+ case DO_DISPATCH_SURFACE_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.dispatchSurfaceChanged(
+ (Integer) args.arg1, (Integer) args.arg2, (Integer) args.arg3);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_BROADCAST_INFO_RESPONSE: {
+ mSessionImpl.notifyBroadcastInfoResponse((BroadcastInfoResponse) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_AD_RESPONSE: {
+ mSessionImpl.notifyAdResponse((AdResponse) msg.obj);
+ break;
+ }
+ case DO_CREATE_MEDIA_VIEW: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_RELAYOUT_MEDIA_VIEW: {
+ mSessionImpl.relayoutMediaView((Rect) msg.obj);
+ break;
+ }
+ case DO_REMOVE_MEDIA_VIEW: {
+ mSessionImpl.removeMediaView(true);
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unhandled message code: " + msg.what);
+ break;
+ }
+ }
+ long durationMs = (System.nanoTime() - startTime) / (1000 * 1000);
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
+ Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration="
+ + durationMs + "ms)");
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
+ // TODO: handle timeout
+ }
+ }
+ }
+
+ @Override
+ public void startInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void stopInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void resetInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESET_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_CREATE_BI_INTERACTIVE_APP, biIAppUri, params));
+ }
+
+ @Override
+ public void destroyBiInteractiveApp(@NonNull String biIAppId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_DESTROY_BI_INTERACTIVE_APP, biIAppId));
+ }
+
+ @Override
+ public void setTeletextAppEnabled(boolean enable) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SET_TELETEXT_APP_ENABLED, enable));
+ }
+
+ @Override
+ public void sendCurrentChannelUri(@Nullable Uri channelUri) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_URI, channelUri));
+ }
+
+ @Override
+ public void sendCurrentChannelLcn(int lcn) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_LCN, lcn));
+ }
+
+ @Override
+ public void sendStreamVolume(float volume) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_STREAM_VOLUME, volume));
+ }
+
+ @Override
+ public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_TRACK_INFO_LIST, tracks));
+ }
+
+ @Override
+ public void sendCurrentTvInputId(@Nullable String inputId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_TV_INPUT_ID, inputId));
+ }
+
+ @Override
+ public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_SEND_SIGNING_RESULT, signingId, result));
+ }
+
+ @Override
+ public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_NOTIFY_ERROR, errMsg, params));
+ }
+
+ @Override
+ public void release() {
+ mSessionImpl.scheduleMediaViewCleanup();
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
+ }
+
+ @Override
+ public void notifyTuned(Uri channelUri) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TUNED, channelUri));
+ }
+
+ @Override
+ public void notifyTrackSelected(int type, final String trackId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_NOTIFY_TRACK_SELECTED, type, trackId));
+ }
+
+ @Override
+ public void notifyTracksChanged(List<TvTrackInfo> tracks) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TRACKS_CHANGED, tracks));
+ }
+
+ @Override
+ public void notifyVideoAvailable() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_VIDEO_AVAILABLE));
+ }
+
+ @Override
+ public void notifyVideoUnavailable(int reason) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_VIDEO_UNAVAILABLE, reason));
+ }
+
+ @Override
+ public void notifyContentAllowed() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_CONTENT_ALLOWED));
+ }
+
+ @Override
+ public void notifyContentBlocked(String rating) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_CONTENT_BLOCKED, rating));
+ }
+
+ @Override
+ public void notifySignalStrength(int strength) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_SIGNAL_STRENGTH, strength));
+ }
+
+ @Override
+ public void setSurface(Surface surface) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, format, width, height, 0));
+ }
+
+ @Override
+ public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_NOTIFY_BROADCAST_INFO_RESPONSE, response));
+ }
+
+ @Override
+ public void notifyAdResponse(AdResponse response) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_RESPONSE, response));
+ }
+
+ @Override
+ public void createMediaView(IBinder windowToken, Rect frame) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame));
+ }
+
+ @Override
+ public void relayoutMediaView(Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_MEDIA_VIEW, frame));
+ }
+
+ @Override
+ public void removeMediaView() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_MEDIA_VIEW));
+ }
+
+ private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+ TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mSessionImpl == null) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+
+ int handled = mSessionImpl.dispatchInputEvent(event, this);
+ if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
+ finishInputEvent(
+ event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 54700b6..f5b4322 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -45,7 +45,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -1416,193 +1415,6 @@
}
}
- /**
- * Implements the internal ITvInteractiveAppSession interface.
- * @hide
- */
- public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
- // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
- private final Session mSessionImpl;
- private InputChannel mChannel;
- private TvInteractiveAppEventReceiver mReceiver;
-
- public ITvInteractiveAppSessionWrapper(
- Context context, Session mSessionImpl, InputChannel channel) {
- this.mSessionImpl = mSessionImpl;
- mChannel = channel;
- if (channel != null) {
- mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
- }
- }
-
- @Override
- public void startInteractiveApp() {
- mSessionImpl.startInteractiveApp();
- }
-
- @Override
- public void stopInteractiveApp() {
- mSessionImpl.stopInteractiveApp();
- }
-
- @Override
- public void resetInteractiveApp() {
- mSessionImpl.resetInteractiveApp();
- }
-
- @Override
- public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
- mSessionImpl.createBiInteractiveApp(biIAppUri, params);
- }
-
- @Override
- public void setTeletextAppEnabled(boolean enable) {
- mSessionImpl.setTeletextAppEnabled(enable);
- }
-
- @Override
- public void destroyBiInteractiveApp(@NonNull String biIAppId) {
- mSessionImpl.destroyBiInteractiveApp(biIAppId);
- }
-
- @Override
- public void sendCurrentChannelUri(@Nullable Uri channelUri) {
- mSessionImpl.sendCurrentChannelUri(channelUri);
- }
-
- @Override
- public void sendCurrentChannelLcn(int lcn) {
- mSessionImpl.sendCurrentChannelLcn(lcn);
- }
-
- @Override
- public void sendStreamVolume(float volume) {
- mSessionImpl.sendStreamVolume(volume);
- }
-
- @Override
- public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
- mSessionImpl.sendTrackInfoList(tracks);
- }
-
- @Override
- public void sendCurrentTvInputId(@Nullable String inputId) {
- mSessionImpl.sendCurrentTvInputId(inputId);
- }
-
- @Override
- public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
- mSessionImpl.sendSigningResult(signingId, result);
- }
-
- @Override
- public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
- mSessionImpl.notifyError(errMsg, params);
- }
-
- @Override
- public void release() {
- mSessionImpl.scheduleMediaViewCleanup();
- mSessionImpl.release();
- }
-
- @Override
- public void notifyTuned(Uri channelUri) {
- mSessionImpl.notifyTuned(channelUri);
- }
-
- @Override
- public void notifyTrackSelected(int type, final String trackId) {
- mSessionImpl.notifyTrackSelected(type, trackId);
- }
-
- @Override
- public void notifyTracksChanged(List<TvTrackInfo> tracks) {
- mSessionImpl.notifyTracksChanged(tracks);
- }
-
- @Override
- public void notifyVideoAvailable() {
- mSessionImpl.notifyVideoAvailable();
- }
-
- @Override
- public void notifyVideoUnavailable(int reason) {
- mSessionImpl.notifyVideoUnavailable(reason);
- }
-
- @Override
- public void notifyContentAllowed() {
- mSessionImpl.notifyContentAllowed();
- }
-
- @Override
- public void notifyContentBlocked(String rating) {
- mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating));
- }
-
- @Override
- public void notifySignalStrength(int strength) {
- mSessionImpl.notifySignalStrength(strength);
- }
-
- @Override
- public void setSurface(Surface surface) {
- mSessionImpl.setSurface(surface);
- }
-
- @Override
- public void dispatchSurfaceChanged(int format, int width, int height) {
- mSessionImpl.dispatchSurfaceChanged(format, width, height);
- }
-
- @Override
- public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
- mSessionImpl.notifyBroadcastInfoResponse(response);
- }
-
- @Override
- public void notifyAdResponse(AdResponse response) {
- mSessionImpl.notifyAdResponse(response);
- }
-
- @Override
- public void createMediaView(IBinder windowToken, Rect frame) {
- mSessionImpl.createMediaView(windowToken, frame);
- }
-
- @Override
- public void relayoutMediaView(Rect frame) {
- mSessionImpl.relayoutMediaView(frame);
- }
-
- @Override
- public void removeMediaView() {
- mSessionImpl.removeMediaView(true);
- }
-
- private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
- TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- if (mSessionImpl == null) {
- // The session has been finished.
- finishInputEvent(event, false);
- return;
- }
-
- int handled = mSessionImpl.dispatchInputEvent(event, this);
- if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
- finishInputEvent(
- event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
- }
- }
- }
- }
-
@SuppressLint("HandlerLeak")
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 62c0d55..556f98c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -643,7 +643,8 @@
return ACQUIRE_SUCCESS;
}
-static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
+ jboolean throwISEOnly) {
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
@@ -666,8 +667,12 @@
res = bufferConsumer->detachBuffer(buffer->mSlot);
if (res != OK) {
ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
- jniThrowRuntimeException(env,
- "nativeDetachImage failed for image!!!");
+ if ((bool) throwISEOnly) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "nativeDetachImage failed for image!!!");
+ } else {
+ jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
+ }
return res;
}
return OK;
@@ -965,7 +970,7 @@
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
{"nativeImageSetup", "(Landroid/media/Image;Z)I", (void*)ImageReader_imageSetup },
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
- {"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
+ {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
{"nativeCreateImagePlanes",
"(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
(void*)ImageReader_createImagePlanes },
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 60f0e9e..cb74cfc 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -86,6 +86,7 @@
?audio/x-matroska mka
?audio/x-pn-realaudio ra
?audio/x-mpeg mp3
+?audio/mp3 mp3
?image/bmp bmp
?image/gif gif
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index a596a9a..7c3f5a5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,8 +28,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider1"
@@ -44,8 +43,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider2"
@@ -60,8 +58,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider3"
@@ -76,6 +73,5 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
new file mode 100644
index 0000000..b4640ee
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
@@ -0,0 +1,85 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:paddingHorizontal="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider1"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider2"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider3"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
new file mode 100644
index 0000000..8975857
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
@@ -0,0 +1,100 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/secondary_app_icon_size"
+ android:orientation="horizontal"
+ android:paddingEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone"/>
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|end"
+ android:minWidth="@dimen/two_target_min_width"
+ android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
new file mode 100644
index 0000000..3a219b9
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
@@ -0,0 +1,38 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:clickable="false">
+
+ <TextView
+ android:id="@+id/apps_top_intro_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:clickable="false"
+ android:longClickable="false"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 12db901..e65f7de 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,7 +55,6 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
@@ -63,7 +62,6 @@
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 ec091bf..f4af9c7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,6 +19,7 @@
import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
import android.app.ActionBar;
+import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
@@ -88,6 +89,12 @@
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
}
disableCollapsingToolbarLayoutScrollingBehavior();
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 a8c7a3f..522de93 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
@@ -22,6 +22,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -110,6 +111,12 @@
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
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 2c1fdd4..42700b3 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,7 +53,6 @@
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/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
new file mode 100644
index 0000000..a2f2510
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -0,0 +1,71 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|top"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:ellipsize="marquee" />
+
+ <com.android.settingslib.widget.LinkTextView
+ android:id="@+id/settingslib_learn_more"
+ android:text="@string/settingslib_learn_more_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:clickable="true"
+ android:visibility="gone"
+ style="@style/TextAppearance.Footer.Title.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
new file mode 100644
index 0000000..35d1323
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -0,0 +1,70 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/frame"
+ android:minHeight="@dimen/settingslib_min_switch_bar_height"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_margin="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <TextView
+ android:id="@+id/switch_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+ android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/MainSwitchText.Settingslib" />
+
+ <ImageView
+ android:id="@+id/restricted_icon"
+ android:layout_width="@dimen/settingslib_restricted_icon_size"
+ android:layout_height="@dimen/settingslib_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
+ android:src="@drawable/settingslib_ic_info"
+ android:visibility="gone" />
+
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
new file mode 100644
index 0000000..bb8ac28
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
@@ -0,0 +1,127 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/radio_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/radio_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 64d100a..906ff2c 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
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-v33/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
new file mode 100644
index 0000000..0b27464
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
@@ -0,0 +1,127 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/selector_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/selector_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
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 2a550ef..8bb56ff 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
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 d4d466a..23aa993 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,7 +43,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -58,7 +57,6 @@
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 0000000..70ce374
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,80 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false"
+ android:baselineAligned="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignLeft="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/PreferenceSummaryTextStyle"/>
+
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingLeft="16dp"
+ android:paddingStart="16dp"
+ android:paddingRight="0dp"
+ android:paddingEnd="0dp"
+ android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/.gitignore b/packages/SettingsLib/Spa/.gitignore
new file mode 100644
index 0000000..b2ed268
--- /dev/null
+++ b/packages/SettingsLib/Spa/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
new file mode 100644
index 0000000..b352b04
--- /dev/null
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -0,0 +1,6 @@
+chaohuiw@google.com
+hanxu@google.com
+kellyz@google.com
+pierreqian@google.com
+
+per-file *.xml = set noparent
diff --git a/packages/SettingsLib/Spa/TEST_MAPPING b/packages/SettingsLib/Spa/TEST_MAPPING
new file mode 100644
index 0000000..ef3db4a
--- /dev/null
+++ b/packages/SettingsLib/Spa/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "SpaLibTests"
+ }
+ ]
+}
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
new file mode 100644
index 0000000..8c97eca
--- /dev/null
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.
+ */
+
+buildscript {
+ ext {
+ minSdk_version = 31
+ compose_version = '1.2.0-alpha04'
+ compose_material3_version = '1.0.0-alpha06'
+ }
+}
+plugins {
+ id 'com.android.application' version '7.3.0-beta04' apply false
+ id 'com.android.library' version '7.3.0-beta04' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
diff --git a/packages/SettingsLib/Spa/codelab/Android.bp b/packages/SettingsLib/Spa/codelab/Android.bp
new file mode 100644
index 0000000..8fbbf9a
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SpaLibCodelab",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "SpaLib",
+ "androidx.compose.runtime_runtime",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ platform_apis: true,
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
new file mode 100644
index 0000000..9a89e5e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.codelab">
+
+ <application
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.SettingsLib.Compose.DayNight">
+ <activity
+ android:name="com.android.settingslib.spa.codelab.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/packages/SettingsLib/Spa/codelab/build.gradle b/packages/SettingsLib/Spa/codelab/build.gradle
new file mode 100644
index 0000000..5251ddd
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.
+ */
+
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa.codelab'
+ compileSdk 33
+
+ defaultConfig {
+ applicationId "com.android.settingslib.spa.codelab"
+ minSdk minSdk_version
+ targetSdk 33
+ versionCode 1
+ versionName "1.0"
+ }
+
+ sourceSets {
+ main {
+ kotlin {
+ srcDir "src"
+ }
+ res.srcDirs = ["res"]
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ implementation(project(":spa"))
+}
diff --git a/packages/SettingsLib/Spa/codelab/res/values/strings.xml b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
new file mode 100644
index 0000000..6fdbba0
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+ 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.
+ -->
+<resources>
+ <!-- Codelab App name. [DO NOT TRANSLATE] -->
+ <string name="app_name" translatable="false">SpaLib Codelab</string>
+</resources>
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
new file mode 100644
index 0000000..eef81e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.settingslib.spa.codelab
+
+import com.android.settingslib.spa.codelab.page.codelabPageRepository
+import com.android.settingslib.spa.framework.SpaActivity
+
+class MainActivity : SpaActivity(codelabPageRepository)
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
new file mode 100644
index 0000000..484b604
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.navigator
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val STRING_PARAM_NAME = "stringParam"
+private const val INT_PARAM_NAME = "intParam"
+
+object ArgumentPageProvider : SettingsPageProvider {
+ override val name = Destinations.Argument
+
+ override val arguments = listOf(
+ navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
+ navArgument(INT_PARAM_NAME) { type = NavType.IntType },
+ )
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ ArgumentPage(
+ stringParam = arguments!!.getString(STRING_PARAM_NAME, "default"),
+ intParam = arguments.getInt(INT_PARAM_NAME),
+ )
+ }
+
+ @Composable
+ fun EntryItem(stringParam: String, intParam: Int) {
+ Preference(object : PreferenceModel {
+ override val title = "Sample page with arguments"
+ override val summary =
+ "$STRING_PARAM_NAME=$stringParam, $INT_PARAM_NAME=$intParam".toState()
+ override val onClick = navigator("${Destinations.Argument}/$stringParam/$intParam")
+ })
+ }
+}
+
+@Composable
+fun ArgumentPage(stringParam: String, intParam: Int) {
+ Column {
+ Preference(object : PreferenceModel {
+ override val title = "String param value"
+ override val summary = stringParam.toState()
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Int param value"
+ override val summary = intParam.toString().toState()
+ })
+
+ ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = intParam + 1)
+
+ ArgumentPageProvider.EntryItem(stringParam = "bar", intParam = intParam + 1)
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ArgumentPagePreview() {
+ SettingsTheme {
+ ArgumentPage(stringParam = "foo", intParam = 0)
+ }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
new file mode 100644
index 0000000..27c70e4
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.codelab.R
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsTheme
+
+object HomePageProvider : SettingsPageProvider {
+ override val name = Destinations.Home
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ HomePage()
+ }
+}
+
+@Composable
+private fun HomePage() {
+ Column {
+ Text(
+ text = stringResource(R.string.app_name),
+ modifier = Modifier.padding(SettingsDimension.itemPadding),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.headlineMedium,
+ )
+
+ PreferencePageProvider.EntryItem()
+
+ ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun HomeScreenPreview() {
+ SettingsTheme {
+ HomePage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
new file mode 100644
index 0000000..862fc1e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.settingslib.spa.codelab.page
+
+import com.android.settingslib.spa.api.SettingsPageRepository
+
+object Destinations {
+ const val Home = "Home"
+ const val Preference = "Preference"
+ const val Argument = "Argument"
+}
+
+val codelabPageRepository = SettingsPageRepository(
+ allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider),
+ startDestination = Destinations.Home,
+)
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
new file mode 100644
index 0000000..81627f6
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.navigator
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlinx.coroutines.delay
+
+object PreferencePageProvider : SettingsPageProvider {
+ override val name = Destinations.Preference
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ PreferencePage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample Preference"
+ override val onClick = navigator(Destinations.Preference)
+ })
+ }
+}
+
+@Composable
+private fun PreferencePage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = "With summary".toState()
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = produceState(initialValue = " ") {
+ delay(1000L)
+ value = "Async summary"
+ }
+ })
+
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Click me"
+ override val summary = derivedStateOf { count.toString() }
+ override val onClick: (() -> Unit) = { count++ }
+ })
+
+ var ticks by remember { mutableStateOf(0) }
+ LaunchedEffect(ticks) {
+ delay(1000L)
+ ticks++
+ }
+ Preference(object : PreferenceModel {
+ override val title = "Ticker"
+ override val summary = derivedStateOf { ticks.toString() }
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Disabled"
+ override val summary = "Disabled".toState()
+ override val enabled = false.toState()
+ })
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun PreferencePagePreview() {
+ SettingsTheme {
+ PreferencePage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gradle.properties b/packages/SettingsLib/Spa/gradle.properties
new file mode 100644
index 0000000..82bd512
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle.properties
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..52095e1
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+#Thu Jul 14 10:36:06 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/packages/SettingsLib/Spa/gradlew.bat b/packages/SettingsLib/Spa/gradlew.bat
new file mode 100644
index 0000000..107acd32
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/SettingsLib/Spa/settings.gradle b/packages/SettingsLib/Spa/settings.gradle
new file mode 100644
index 0000000..1b69c1e
--- /dev/null
+++ b/packages/SettingsLib/Spa/settings.gradle
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "SpaLib"
+include ':spa'
+include ':codelab'
+include ':tests'
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
new file mode 100644
index 0000000..463c076
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SpaLib",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.navigation_navigation-compose",
+ "com.google.android.material_material",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/spa/AndroidManifest.xml b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
new file mode 100644
index 0000000..410bcdb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest package="com.android.settingslib.spa" />
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
new file mode 100644
index 0000000..60794c8
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa'
+ compileSdk 33
+
+ defaultConfig {
+ minSdk minSdk_version
+ targetSdk 33
+ }
+
+ sourceSets {
+ main {
+ kotlin {
+ srcDir "src"
+ }
+ res.srcDirs = ["res"]
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ api "androidx.compose.material3:material3:$compose_material3_version"
+ api "androidx.compose.material:material-icons-extended:$compose_version"
+ api "androidx.compose.runtime:runtime-livedata:$compose_version"
+ api "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ api 'androidx.navigation:navigation-compose:2.5.0'
+ api 'com.google.android.material:material:1.6.1'
+ debugApi "androidx.compose.ui:ui-tooling:$compose_version"
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
new file mode 100644
index 0000000..8b52b50
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<resources>
+
+ <style name="Theme.SettingsLib.Compose.DayNight" />
+</resources>
diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml
new file mode 100644
index 0000000..01f9ea5
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+<resources>
+
+ <style name="Theme.SettingsLib.Compose" parent="Theme.Material3.DayNight.NoActionBar">
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
+ <style name="Theme.SettingsLib.Compose.DayNight">
+ <item name="android:windowLightStatusBar">true</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
new file mode 100644
index 0000000..0ad0003
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.settingslib.spa.api
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.navigation.NamedNavArgument
+
+/**
+ * An SettingsPageProvider represent a Settings page.
+ */
+interface SettingsPageProvider {
+
+ /** The page name without arguments. */
+ val name: String
+
+ /** The page arguments, default is no arguments. */
+ val arguments: List<NamedNavArgument>
+ get() = emptyList()
+
+ /** The [Composable] used to render this page. */
+ @Composable
+ fun Page(arguments: Bundle?)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
new file mode 100644
index 0000000..ce39f4f
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.settingslib.spa.api
+
+data class SettingsPageRepository(
+ val allPages: List<SettingsPageProvider>,
+ val startDestination: String,
+)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
new file mode 100644
index 0000000..35112ad
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.remember
+import androidx.navigation.NavHostController
+
+interface NavControllerWrapper {
+ fun navigate(route: String)
+ fun navigateUp()
+}
+
+@Composable
+fun NavHostController.localNavController() =
+ LocalNavController provides remember { NavControllerWrapperImpl(this) }
+
+val LocalNavController = compositionLocalOf<NavControllerWrapper> {
+ object : NavControllerWrapper {
+ override fun navigate(route: String) {}
+
+ override fun navigateUp() {}
+ }
+}
+
+@Composable
+fun navigator(route: String): () -> Unit {
+ val navController = LocalNavController.current
+ return { navController.navigate(route) }
+}
+
+internal class NavControllerWrapperImpl(
+ private val navController: NavHostController,
+) : NavControllerWrapper {
+ override fun navigate(route: String) {
+ navController.navigate(route)
+ }
+
+ override fun navigateUp() {
+ navController.navigateUp()
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
new file mode 100644
index 0000000..22d85e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+
+/**
+ * Remember the [State] initialized with the [this].
+ */
+@Composable
+fun <T> T.toState(): State<T> = remember { stateOf(this) }
+
+/**
+ * Return a new [State] initialized with the passed in [value].
+ */
+fun <T> stateOf(value: T) = object : State<T> {
+ override val value = value
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
new file mode 100644
index 0000000..28a5899e
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.settingslib.spa.framework
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.android.settingslib.spa.api.SettingsPageProvider
+import com.android.settingslib.spa.api.SettingsPageRepository
+import com.android.settingslib.spa.theme.SettingsTheme
+
+open class SpaActivity(
+ private val settingsPageRepository: SettingsPageRepository,
+) : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MainContent()
+ }
+ }
+
+ @Composable
+ private fun MainContent() {
+ SettingsTheme {
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, settingsPageRepository.startDestination) {
+ for (page in settingsPageRepository.allPages) {
+ composable(
+ route = page.route,
+ arguments = page.arguments,
+ ) { navBackStackEntry ->
+ page.Page(navBackStackEntry.arguments)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private val SettingsPageProvider.route: String
+ get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
new file mode 100644
index 0000000..8cd9184
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.settingslib.spa.theme
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+internal fun materialColorScheme(isDarkTheme: Boolean): ColorScheme {
+ val context = LocalContext.current
+ return remember(isDarkTheme) {
+ when {
+ isDarkTheme -> dynamicDarkColorScheme(context)
+ else -> dynamicLightColorScheme(context)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
new file mode 100644
index 0000000..51e75c5
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.settingslib.spa.theme
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.ui.unit.dp
+
+object SettingsDimension {
+ val itemIconContainerSize = 72.dp
+ val itemPaddingStart = 24.dp
+ val itemPaddingEnd = 16.dp
+ val itemPaddingVertical = 16.dp
+ val itemPadding = PaddingValues(
+ start = itemPaddingStart,
+ top = itemPaddingVertical,
+ end = itemPaddingEnd,
+ bottom = itemPaddingVertical,
+ )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
new file mode 100644
index 0000000..5f4da8a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.settingslib.spa.theme
+
+object SettingsOpacity {
+ const val Full = 1f
+ const val Disabled = 0.38f
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
new file mode 100644
index 0000000..fce9f2b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.settingslib.spa.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+
+/**
+ * The Material 3 Theme for Settings.
+ */
+@Composable
+fun SettingsTheme(content: @Composable () -> Unit) {
+ val isDarkTheme = isSystemInDarkTheme()
+ val colorScheme = materialColorScheme(isDarkTheme)
+
+ MaterialTheme(colorScheme = colorScheme) {
+ content()
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
new file mode 100644
index 0000000..d415e9b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Divider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsOpacity
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BaseLayout(
+ title: String,
+ subTitle: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ enabled: State<Boolean> = true.toState(),
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ widget: @Composable () -> Unit = {},
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(end = paddingEnd),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ val alphaModifier =
+ Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled)
+ BaseIcon(icon, alphaModifier, paddingStart)
+ Titles(
+ title = title,
+ subTitle = subTitle,
+ modifier = alphaModifier
+ .weight(1f)
+ .padding(vertical = paddingVertical),
+ )
+ widget()
+ }
+}
+
+@Composable
+private fun BaseIcon(
+ icon: @Composable (() -> Unit)?,
+ modifier: Modifier,
+ paddingStart: Dp,
+) {
+ if (icon != null) {
+ Box(
+ modifier = modifier.size(SettingsDimension.itemIconContainerSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ icon()
+ }
+ } else {
+ Spacer(modifier = Modifier.width(width = paddingStart))
+ }
+}
+
+// Extracts a scope to avoid frequent recompose outside scope.
+@Composable
+private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) {
+ Column(modifier) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleMedium,
+ )
+ subTitle()
+ }
+}
+
+@Preview
+@Composable
+private fun BaseLayoutPreview() {
+ SettingsTheme {
+ BaseLayout(
+ title = "Title",
+ subTitle = {
+ Divider(thickness = 10.dp)
+ }
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
new file mode 100644
index 0000000..563a47a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.BatteryChargingFull
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BasePreference(
+ title: String,
+ summary: State<String>,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ enabled: State<Boolean> = true.toState(),
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ widget: @Composable () -> Unit = {},
+) {
+ BaseLayout(
+ title = title,
+ subTitle = {
+ if (summary.value.isNotEmpty()) {
+ Text(
+ text = summary.value,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+ },
+ modifier = modifier,
+ icon = icon,
+ enabled = enabled,
+ paddingStart = paddingStart,
+ paddingEnd = paddingEnd,
+ paddingVertical = paddingVertical,
+ widget = widget,
+ )
+}
+
+@Preview
+@Composable
+private fun BasePreferencePreview() {
+ SettingsTheme {
+ BasePreference(
+ title = "Screen Saver",
+ summary = "Clock".toState(),
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun BasePreferenceIconPreview() {
+ SettingsTheme {
+ BasePreference(
+ title = "Screen Saver",
+ summary = "Clock".toState(),
+ icon = {
+ Icon(imageVector = Icons.Outlined.BatteryChargingFull, contentDescription = null)
+ },
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
new file mode 100644
index 0000000..ee20280
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.clickable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.stateOf
+
+/**
+ * The widget model for [Preference] widget.
+ */
+interface PreferenceModel {
+ /**
+ * The title of this [Preference].
+ */
+ val title: String
+
+ /**
+ * The summary of this [Preference].
+ */
+ val summary: State<String>
+ get() = stateOf("")
+
+ /**
+ * Indicates whether this [Preference] is enabled.
+ *
+ * Disabled [Preference] will be displayed in disabled style.
+ */
+ val enabled: State<Boolean>
+ get() = stateOf(true)
+
+ /**
+ * The on click handler of this [Preference].
+ *
+ * This also indicates whether this [Preference] is clickable.
+ */
+ val onClick: (() -> Unit)?
+ get() = null
+}
+
+/**
+ * Preference widget.
+ *
+ * Data is provided through [PreferenceModel].
+ */
+@Composable
+fun Preference(model: PreferenceModel) {
+ val modifier = model.onClick?.let { onClick ->
+ Modifier.clickable(enabled = model.enabled.value) { onClick() }
+ } ?: Modifier
+ BasePreference(
+ title = model.title,
+ summary = model.summary,
+ modifier = modifier,
+ enabled = model.enabled,
+ )
+}
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
new file mode 100644
index 0000000..037d8c4
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SpaLibTests",
+ test_suites: ["device-tests"],
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "SpaLib",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Spa/tests/AndroidManifest.xml b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
new file mode 100644
index 0000000..c224caf
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SpaLib"
+ android:targetPackage="com.android.settingslib.spa.tests">
+ </instrumentation>
+</manifest>
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
new file mode 100644
index 0000000..707017e
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa.tests'
+ compileSdk 33
+
+ defaultConfig {
+ minSdk minSdk_version
+ targetSdk 33
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ sourceSets {
+ androidTest {
+ kotlin {
+ srcDir "src"
+ }
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ androidTestImplementation(project(":spa"))
+ androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
+ androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
new file mode 100644
index 0000000..4097946
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.toState
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_enabled_withEffect() {
+ composeTestRule.setContent {
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = derivedStateOf { count.toString() }
+ override val onClick: (() -> Unit) = { count++ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").performClick()
+ composeTestRule.onNodeWithText("1").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_disabled_noEffect() {
+ composeTestRule.setContent {
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = derivedStateOf { count.toString() }
+ override val enabled = false.toState()
+ override val onClick: (() -> Unit) = { count++ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").performClick()
+ composeTestRule.onNodeWithText("0").assertIsDisplayed()
+ }
+}
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
new file mode 100644
index 0000000..6046d91
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:longClickable="false"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index b2a9037..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,6 +33,5 @@
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 ac5807d..2c35772 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -53,7 +52,6 @@
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/TwoTargetPreference/res/layout-v33/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
new file mode 100644
index 0000000..0bca9ab
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
@@ -0,0 +1,74 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10"/>
+
+ </RelativeLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_access_point.xml b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
new file mode 100644
index 0000000..81bfeffd
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
@@ -0,0 +1,110 @@
+<?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.
+ -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="48dp"
+ settings:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+ <ImageButton
+ android:id="@+id/icon_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone">
+ </ImageButton>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
new file mode 100644
index 0000000..7ad018c
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
@@ -0,0 +1,97 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="@android:color/transparent"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/checkbox_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <include layout="@layout/preference_widget_checkbox" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
new file mode 100644
index 0000000..31e9696
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -0,0 +1,97 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <com.android.internal.widget.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="48dp"
+ android:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ <TextView
+ android:id="@+id/additional_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/summary"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:visibility="gone" />
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 4ad9d80..802d604 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,7 +65,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -77,7 +76,6 @@
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 cbe49cd..f512f9b 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,7 +62,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -74,7 +73,6 @@
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 edea144..169ae97 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,7 +52,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -63,7 +62,6 @@
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"
@@ -74,7 +72,6 @@
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-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 7165c14..1de7668 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Gebruik stelselkeuse (verstek)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Gebruik stelselkeuse (verstek)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gebruik stelselkeuse (verstek)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 264faad..fe8030f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoertoestel"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling van kontakte en oproepgeskiedenis"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruik vir deling van kontakte en oproepgeskiedenis"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling van internetverbinding"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksboodskappe"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luggehalte"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Uitsaai-inligting"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Huiskontroles"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Kies \'n profielprent"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Verstekgebruikerikoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fisieke sleutelbord"</string>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 1108c82..a900d13 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ኦዲዮ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ኦዲዮ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ኦዲዮ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ኦዲዮ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
<item msgid="8003118270854840095">"44.1 ኪኸ"</item>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b68cbd7..d3c034f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ግቤት መሣሪያ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"የበይነመረብ ድረስ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"የእውቂያዎች እና የጥሪ ታሪክ ማጋራት"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"እውቂያዎችን እና የጥሪ ታሪክን ለማጋራት ይጠቀሙበት"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"የበይነ መረብ ተያያዥ ማጋሪያ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"የጽሑፍ መልዕክቶች"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"የሲም መዳረሻ"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"የአየር ሁኔታ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"የአየር ጥራት"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"የCast መረጃ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"የመገለጫ ሥዕል ይምረጡ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ነባሪ የተጠቃሚ አዶ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"አካላዊ ቁልፍ ሰሌዳ"</string>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index eb4be38..8f7d7d2 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"استخدام اختيار النظام (تلقائي)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"استخدام اختيار النظام (تلقائي)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"استخدام اختيار النظام (تلقائي)"</item>
<item msgid="8003118270854840095">"44.1 كيلو هرتز"</item>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c8b263b..6783654 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"استخدام الإنترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"مشاركة جهات الاتصال وسجل المكالمات"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استخدام إعدادات بلوتوث لمشاركة جهات الاتصال وسجل المكالمات"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"مشاركة اتصال الإنترنت"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"الرسائل النصية"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"الوصول إلى شريحة SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"الطقس"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"جودة الهواء"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"معلومات البث"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"اختيار صورة الملف الشخصي"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"رمز المستخدم التلقائي"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"لوحة مفاتيح خارجية"</string>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index df23f67..4c879d0 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
- <item msgid="4055460186095649420">"এছবিচি"</item>
- <item msgid="720249083677397051">"এএচি"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিঅ\'"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিঅ’"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
- <item msgid="9024885861221697796">"এছবিচি"</item>
- <item msgid="4688890470703790013">"এএচি"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিঅ’"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিঅ’"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
<item msgid="8003118270854840095">"৪৪.১ কিল\'হাৰ্টজ"</item>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ea1aff0..543c703 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইচ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইণ্টাৰনেট সংযোগ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰা"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰাৰ বাবে ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিম প্ৰৱেশ"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"বতৰ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"বায়ুৰ গুণগত মান"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাষ্টৰ তথ্য"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"কায়িক কীব’ৰ্ড"</string>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 5163791..48974a7 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sistem Seçimini istifadə edin (Defolt)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sistem Seçimini istifadə edin (Defolt)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sistem Seçimini istifadə edin (Defolt)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 88333e82..f3e5d95 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Daxiletmə cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternetə giriş"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktlar və zəng tarixçəsi paylaşımı"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar və zəng tarixçəsi paylaşımı üçün istifadə edin"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"internet bağlantı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mətn Mesajları"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hava"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havanın keyfiyyəti"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayım məlumatı"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil şəkli seçin"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Defolt istifadəçi ikonası"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziki klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 5cc43f6..337da26 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Koristi izbor sistema (podrazumevano)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Koristi izbor sistema (podrazumevano)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Koristi izbor sistema (podrazumevano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a84d019..6a880aa 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup Internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje kontakata i istorije poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Koristite za deljenje kontakata i istorije poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet vazduha"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o prebacivanju"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Podrazumevana ikona korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index 6259c2d..d843629 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Выбар сістэмы (стандартны)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Выбар сістэмы (стандартны)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Аўдыя <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Выбар сістэмы (стандартны)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 37f8410..a902426 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Прылада ўводу"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ у інтэрнэт"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Абагульванне кантактаў і гісторыі выклікаў"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ужываць для абагульвання кантактаў і гісторыі выклікаў"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Прадастаўленне доступу да Інтэрнэту"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Тэкставыя паведамленні"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ да SIM-карты"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Надвор\'е"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якасць паветра"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Даныя пра трансляцыю"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Выберыце відарыс профілю"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Стандартны карыстальніцкі значок"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Фізічная клавіятура"</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 49e66c0..1aad6ca7 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Използване на сист. избор (стандартно)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"Разширено аудиокодиране (AAC)"</item>
- <item msgid="1049450003868150455">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
- <item msgid="2908219194098827570">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Използване на сист. избор (стандартно)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"Разширено аудиокодиране (AAC)"</item>
- <item msgid="8627333814413492563">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
- <item msgid="3517061573669307965">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Използване на сист. избор (стандартно)"</item>
<item msgid="8003118270854840095">"44,1 кХц"</item>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index f2cf69e..c512366 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделяне на контактите и ист. на обажд."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Служи за споделяне на контактите и историята на обажданията"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделяне на връзката с интернет"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстови съобщения"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Достъп до SIM картата"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Времето"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество на въздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Предаване: Инф."</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Изберете снимка на потребителския профил"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Икона за основния потребител"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физическа клавиатура"</string>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index fdb611b..5e6bb95 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
<item msgid="8003118270854840095">"৪৪.১ kHz"</item>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 801fb34..23eed04 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইস"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইন্টারনেট অ্যাক্সেস"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"পরিচিতি এবং কলের ইতিহাস শেয়ার করা"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"পরিচিতি ও কলের ইতিহাস শেয়ার করার জন্য ব্যবহার করুন"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইন্টারনেট কানেকশন শেয়ার করা হচ্ছে"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"এসএমএস"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"সিম অ্যাক্সেস"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"আবহাওয়া"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"এয়ার কোয়ালিটি"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাস্ট সম্পর্কিত তথ্য"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"হোম কন্ট্রোল"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"একটি প্রোফাইল ছবি বেছে নিন"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ডিফল্ট ব্যবহারকারীর আইকন"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ফিজিক্যাল কীবোর্ড"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 32edef1..262a35f 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Korištenje odabira sistema (zadano)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Korištenje odabira sistema (zadano)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Korištenje odabira sistema (zadano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 61082f6..78a484f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i historije poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotrijebite za dijeljenje kontakata i historije poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o emitiranju"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Zadana ikona korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index a267af8..8c34a1f 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Utilitza la selecció del sistema (predeterminada)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Utilitza la selecció del sistema (predeterminada)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utilitza la selecció del sistema (predeterminada)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 881f9d4..606a379 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositiu d\'entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accés a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartició de contactes i trucades"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Utilitza per compartir contactes i l\'historial de trucades"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartició de connexió d\'Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Missatges de text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Temps"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualitat de l\'aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informació d\'emissió"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Tria una foto de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona d\'usuari predeterminat"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclat físic"</string>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 3eeae64..90bcaa4 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Použít systémový výběr (výchozí)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Použít systémový výběr (výchozí)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Zvuk <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Použít systémový výběr (výchozí)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7f314bd..37c0bb4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupní zařízení"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Přístup k internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Sdílení kontaktů a historie volání"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používat ke sdílení kontaktů a historie hovorů"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Sdílení internetového připojení"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové zprávy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Počasí"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info o odesílání"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Vyberte profilový obrázek"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Výchozí uživatelská ikona"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnice"</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 58ca722..155104ae 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Brug systemvalg (standard)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Brug systemvalg (standard)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Brug systemvalg (standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index c470b81..d80e1ec 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inputenhed"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetadgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling af kontakter og opkaldshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Brug til deling af kontakter og opkaldshistorik"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling af internetforbindelse"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-beskeder"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vejr"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast-oplysninger"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Vælg et profilbillede"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon for standardbruger"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index a8eb3f6..31126a8 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Systemauswahl verwenden (Standard)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Systemauswahl verwenden (Standard)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Systemauswahl verwenden (Standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 34a622b..d4fafe1 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Eingabegerät"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetzugriff"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Teilen von Kontakten und der Anrufliste"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Zum Teilen von Kontakten und der Anrufliste verwenden"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Freigabe der Internetverbindung"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Wetter"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftqualität"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Streaming-Info"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profilbild auswählen"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standardmäßiges Nutzersymbol"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physische Tastatur"</string>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index ccd06fa..70000e1 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index fdc7419..aa613a5 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Συσκευή εισόδου"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Πρόσβαση στο Διαδίκτυο"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Κοινοποίηση επαφών και ιστορικού κλήσεων"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Χρήση για την κοινοποίηση επαφών και του ιστορικού κλήσεων"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Κοινή χρήση σύνδεσης στο Διαδίκτυο"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Μηνύματα κειμένου"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Πρόσβαση SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Καιρός"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ποιότητα αέρα"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Πληροφορίες ηθοποιών"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Επιλογή φωτογραφίας προφίλ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Προεπιλεγμένο εικονίδιο χρήστη"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Φυσικό πληκτρολόγιο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Use system selection (default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Use system selection (default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Use system selection (default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Use system selection (default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index d88bb0e..8650b77 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Use system selection (default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Use system selection (default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 697e49a..fc6f791 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Use system selection (default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Use system selection (default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 234109b..62c8e21 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index aca3eb4..34db380 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Use System Selection (Default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Use System Selection (Default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use System Selection (Default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 5d775d3..21697b9 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -660,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air Quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast Info"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 6a926d2..9b1aa3a 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar selección del sistema (predeterminado)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usar selección del sistema (predeterminado)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar selección del sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 98c1400..27cd0ab 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llam."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso para compartir contactos e historial de llamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info de reparto"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Control de la casa"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Elige una foto de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícono de usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 7c37fa5..0677864 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar preferencia del sistema (predeterminado)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usar preferencia del sistema (predeterminado)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar preferencia del sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 90c4fb8..d221c8d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para compartir los contactos y el historial de llamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Tiempo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de emisión"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Elige una imagen de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icono de usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index 14d0fd1..d986ecf 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Süsteemi valiku kasutamine (vaikeseade)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Süsteemi valiku kasutamine (vaikeseade)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Süsteemi valiku kasutamine (vaikeseade)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index a6c787a..f32ae23 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktide ja kõneajaloo jagamine"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kasutage kontaktide ja kõneajaloo jagamiseks"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ilm"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Õhukvaliteet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Osatäitjate teave"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Valige profiilipilt"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Vaikekasutajaikoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Füüsiline klaviatuur"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index cc47e27..d166e1b 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Erabili sistema-hautapena (lehenetsia)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Erabili sistema-hautapena (lehenetsia)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Erabili sistema-hautapena (lehenetsia)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 87cfaa8..bcb57e8 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktuak eta deien historia partekatzeko aukera"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Erabili kontaktuetarako eta deien historia partekatzeko"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneteko konexioa partekatzea"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Testu-mezuak"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Eguraldia"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Airearen kalitatea"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Igorpenari buruzko informazioa"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Aukeratu profileko argazki bat"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Erabiltzaile lehenetsiaren ikonoa"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teklatu fisikoa"</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index d76389b..b7761dd 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"استفاده از انتخاب سیستم (پیشفرض)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"استفاده از انتخاب سیستم (پیشفرض)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"استفاده از انتخاب سیستم (پیشفرض)"</item>
<item msgid="8003118270854840095">"۴۴٫۱ کیلوهرتز"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 51d74dd..6abe873 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"دستگاه ورودی"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"دسترسی به اینترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"همرسانی مخاطبین و سابقه تماس"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"استفاده برای همرسانی مخاطبین و سابقه تماس"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"اشتراکگذاری اتصال اینترنت"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"پیامهای نوشتاری"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"دسترسی سیمکارت"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"آبوهوا"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"کیفیت هوا"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"اطلاعات پخش محتوا"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"انتخاب عکس نمایه"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"نماد کاربر پیشفرض"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"صفحهکلید فیزیکی"</string>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index f828186..2969892 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Käytä järjestelmän valintaa (oletus)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ‑ääni"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Käytä järjestelmän valintaa (oletus)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ‑ääni"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Käytä järjestelmän valintaa (oletus)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index c0f90b8..2f3436e 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Syöttölaite"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetyhteys"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Yhteystietojen ja soittohistorian jako"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Käytä yhteystiedoissa ja soittohistorian jakamiseen"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetyhteyden jakaminen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstiviestit"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Sää"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ilmanlaatu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Striimaustiedot"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Valitse profiilikuva"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Oletuskäyttäjäkuvake"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyysinen näppäimistö"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 50c1bcb..12acbb6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Utiliser sélect. du système (par défaut)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Utiliser sélect. du système (par défaut)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utiliser sélect. du système (par défaut)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 14515cd..28b3cd9 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage des contacts et des appels"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Sert à partager des contacts et l\'historique des appels"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Messages texte"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info diffusion"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Choisir une photo de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icône d\'utilisateur par défaut"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index 6343f0d..80ac7e4 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Utiliser la sélection du système (par défaut)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Utiliser la sélection du système (par défaut)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utiliser la sélection du système (par défaut)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 19ea6dd..da1497a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage contacts/historique des appels"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"À utiliser pour partage des contacts/historique des appels"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Infos distribution"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Choisissez une photo de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icône de l\'utilisateur par défaut"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index 22fb223..b6cf48e 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar selección do sistema (predeterminado)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usa a selección do sistema (predeterminado)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar selección do sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 94fa72a..7822749 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e hist. de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso da opción de compartir contactos e historial de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Uso compartido da conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensaxes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"O tempo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidade do aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Datos da emisión"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolle unha imaxe do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona do usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index 318b5f5..7e668e7 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index a474d82..cebb1fe 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ઇનપુટ ડિવાઇસ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ઇન્ટરનેટ ઍક્સેસ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"સંપર્કો અને કૉલ ઇતિહાસની શેરિંગ માટે ઉપયોગ કરો"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"હવામાન"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"હવાની ક્વૉલિટી"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"કાસ્ટ વિશેની માહિતી"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"પ્રોફાઇલ ફોટો પસંદ કરો"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ભૌતિક કીબોર્ડ"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 61a8f92..13da75b 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"सिस्टम से चुने जाने का इस्तेमाल करें (डिफ़ॉल्ट)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 55fc547..417d2b2 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट ऐक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क और कॉल का इतिहास शेयर करें"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"इसका इस्तेमाल संपर्क और कॉल का इतिहास शेयर करने के लिए करें"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन साझाकरण"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"लेख संदेश"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम ऐक्सेस"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवा की क्वालिटी"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टिंग की जानकारी"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफ़ाइल फ़ोटो चुनें"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index c979bc4..0e66858 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Upotreba odabira sustava (zadano)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Upotreba odabira sustava (zadano)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Upotreba odabira sustava (zadano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 55a3e5e..267000f 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i povijesti poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotreba za dijeljenje kontakata i povijesti poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internetske veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvaliteta zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Inform. o emitiranju"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Odabir profilne slike"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona zadanog korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 230e554..a5f37ea 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Rendszerérték (alapértelmezett)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Rendszerérték (alapértelmezett)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Rendszerérték (alapértelmezett)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 2454d1c..69ddad8 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Névjegyek és hívásnapló megosztása"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Névjegyek és hívásnapló megosztásához használható"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Időjárás"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Levegőminőség"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Átküldési információ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profilkép választása"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Alapértelmezett felhasználó ikonja"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizikai billentyűzet"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2a9c544..50d7c7f 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> աուդիո"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> աուդիո"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> աուդիո"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> աուդիո"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
<item msgid="8003118270854840095">"44,1 կՀց"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 8966551..d8efbd3 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ներմուծման սարք"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ինտերնետի հասանելիություն"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Կիսվել կոնտակտներով/զանգերի պատմությամբ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Օգտագործել՝ կոնտակտներով/զանգերի պատմությամբ կիսվելու համար"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ինտերնետ կապի տարածում"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS հաղորդագրություններ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Եղանակ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Օդի որակը"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Հեռարձակման տվյալներ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Պրոֆիլի նկար ընտրեք"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Օգտատիրոջ կանխադրված պատկերակ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Ֆիզիկական ստեղնաշար"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 6349e53..5b0ad98 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gunakan Pilihan Sistem (Default)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c525317..029ad8b 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Berbagi kontak dan histori panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gunakan untuk berbagi kontak dan histori panggilan"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualitas Udara"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info Transmisi"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Pilih foto profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna default"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Keyboard fisik"</string>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 730fcaf..9d481f8 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Nota val kerfisins (sjálfgefið)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Nota val kerfisins (sjálfgefið)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Nota val kerfisins (sjálfgefið)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index c06d2f0..ccf06b0 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inntakstæki"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetaðgangur"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Tengiliðir, SMS-skilaboð og símtalaferill"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Nota til að deila tengiliðum og símtalaferli"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deiling nettengingar"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textaskilaboð"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Veður"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Loftgæði"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Útsendingaruppl."</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Veldu prófílmynd"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Tákn sjálfgefins notanda"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Vélbúnaðarlyklaborð"</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index b7e114f..62450da3 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usa selezione di sistema (predefinita)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usa selezione di sistema (predefinita)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usa selezione di sistema (predefinita)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b3ac25a..3811152 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivis. contatti e cronologia chiamate"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usa per condivisione di contatti e cronologia chiamate"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualità dell\'aria"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info sul cast"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Scegli un\'immagine del profilo"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona dell\'utente predefinito"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fisica"</string>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 587016f..49f3fcf 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
<item msgid="8003118270854840095">"44.1 קילו-הרץ"</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f021b37..3421fb5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"שיתוף אנשי הקשר והיסטוריית השיחות"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ההגדרה משמשת לשיתוף של אנשי הקשר והיסטוריית השיחות"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"שיתוף חיבור לאינטרנט"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"הודעות טקסט"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"גישה ל-SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"מזג אוויר"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"איכות האוויר"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"פרטי ההעברה"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"בית חכם"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"בחירה של תמונת פרופיל"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"סמל המשתמש שמוגדר כברירת מחדל"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"מקלדת פיזית"</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index ad84d9e..d73cc43 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"システムの選択(デフォルト)を使用"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"システムの選択(デフォルト)を使用"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"システムの選択(デフォルト)を使用"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2994e4d..e7d2cf8 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"入力デバイス"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"インターネットアクセス"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"連絡先と通話履歴の共有"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"連絡先と通話履歴の共有に使用します"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"インターネット接続の共有"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"テキスト メッセージ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMアクセス"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天気"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"大気質"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"キャスト情報"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"プロフィール写真の選択"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"デフォルト ユーザー アイコン"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"物理キーボード"</string>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index 01f1dcc7..c0d6f97 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
<item msgid="8003118270854840095">"44,1 კჰც"</item>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 0c3277b..09f5471 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"შეყვანის მოწყობილობა"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ინტერნეტზე წვდომა"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"კონტაქტ. და საუბრის ისტორიის გაზიარება"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"გამოიყენეთ კონტაქტებისა და საუბრის ისტორიის გასაზიარებლად"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ინტერნეტ კავშირის გაზიარება"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ტექსტური შეტყობინებები"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM წვდომა"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ამინდი"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ჰაერის ხარისხი"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ტრანსლირების ინფო"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"სახლის მართვა"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"აირჩიეთ პროფილის სურათი"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"მომხმარებლის ნაგულისხმევი ხატულა"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ფიზიკური კლავიატურა"</string>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index 33fd25b..fc998e7 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Жүйенің таңдағанын алу (әдепкі)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Жүйенің таңдағанын алу (әдепкі)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Жүйенің таңдағанын алу (әдепкі)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 63c9575..3281303 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Контактілер мен қоңыраулар тарихын бөлісу"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Контактілер мен қоңыраулар тарихын бөлісу үшін пайдалану"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланысын ортақ қолдану"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Мәтіндік хабарлар"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картасына кіру"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ауа райы"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ауа сапасы"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Трансляция ақпараты"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Профиль суретін таңдау"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Әдепкі пайдаланушы белгішесі"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Пернетақта"</string>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index ef3aa52..a005f4d 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ប្រើការជ្រើសរើសប្រព័ន្ធ (លំនាំដើម)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ប្រើការជ្រើសរើសប្រព័ន្ធ (លំនាំដើម)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>សំឡេង <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ប្រើការជ្រើសរើសប្រព័ន្ធ (លំនាំដើម)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9b9228a..87c1aa2 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរឯកសារ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ឧបករណ៍បញ្ចូល"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ការចូលប្រើអ៊ីនធឺណិត"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ការប្រើសម្រាប់ការចែករំលែកទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ចែករំលែកការតភ្ជាប់អ៊ីនធឺណិត"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"សារជាអក្សរ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ការចូលដំណើរការស៊ីម"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"អាកាសធាតុ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"គុណភាពខ្យល់"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ព័ត៌មានអំពីការបញ្ជូន"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ជ្រើសរើសរូបភាពកម្រងព័ត៌មាន"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ក្ដារចុចរូបវន្ត"</string>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 6623564..b6014ce 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ಆಡಿಯೋ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ಆಡಿಯೋ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ಆಡಿಯೋ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ಆಡಿಯೋ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
@@ -184,7 +172,7 @@
<item msgid="7300881231043255746">"ಕೆರ್ನಲ್ ಮಾತ್ರ"</item>
</string-array>
<string-array name="select_logpersist_summaries">
- <item msgid="97587758561106269">"ಆಫ್ ಆಗಿದೆ"</item>
+ <item msgid="97587758561106269">"ಆಫ್"</item>
<item msgid="7126170197336963369">"ಎಲ್ಲಾ ಲಾಗ್ ಬಫರ್ಗಳು"</item>
<item msgid="7167543126036181392">"ಎಲ್ಲಾ ಆದರೆ ರೇಡಿಯೊ ಲಾಗ್ ಬಫರ್ಗಳು"</item>
<item msgid="5135340178556563979">"ಕೆರ್ನಲ್ ಲಾಗ್ ಬಫರ್ ಮಾತ್ರ"</item>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 7071598..ab437e8 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ಇನ್ಪುಟ್ ಸಾಧನ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ಸಂಪರ್ಕಗಳು ಹಾಗೂ ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ಸಂಪರ್ಕಗಳು ಮತ್ತು ಕರೆ ಇತಿಹಾಸ ಹಂಚಿಕೆಗಾಗಿ ಬಳಸಿ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ಪಠ್ಯ ಸಂದೇಶಗಳು"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ಸಿಮ್ ಪ್ರವೇಶ"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ಹವಾಮಾನ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ವಾಯು ಗುಣಮಟ್ಟ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ಬಿತ್ತರಿಸಿದ ಮಾಹಿತಿ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ಡೀಫಾಲ್ಟ್ ಬಳಕೆದಾರರ ಐಕಾನ್"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index a207706..7138113 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"시스템 설정 사용(기본)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"시스템 설정 사용(기본)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"시스템 설정 사용(기본)"</item>
<item msgid="8003118270854840095">"44.1kHz"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 6ba289d..2b8ab75 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"연락처 및 통화 기록 공유"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"연락처 및 통화 기록 공유에 사용"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"인터넷 연결 공유"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"문자 메시지"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 액세스"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"날씨"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"대기 상태"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"전송 정보"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"프로필 사진 선택하기"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"기본 사용자 아이콘"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"물리적 키보드"</string>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 67cdc7c..40271f7 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"карта13"</item>
<item msgid="8147982633566548515">"карта14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Система тандаганды колдонуу (демейки)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Система тандаганды колдонуу (демейки)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3eb43fa..e993e3f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Киргизүү түзмөгү"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке мүмкүнчүлүк алуу"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Байланыштарды жана чалуу таржымалын бөлүшүү"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Байланыштарды жана чалуу таржымалын бөлүшүү үчүн колдонуу"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланышын бөлүшүү"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS билдирүүлөрү"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Аба ырайы"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Абанын сапаты"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Тышкы экранга чыгаруу маалыматы"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Профилдин сүрөтүн тандоо"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Демейки колдонуучунун сүрөтчөсү"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Аппараттык баскычтоп"</string>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index 56a9741..792ca39 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"ສຽງ <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 2dcad1f..d295932 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ໃຊ້ສໍາລັບແບ່ງປັນລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ການແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ຂໍ້ຄວາມ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ການເຂົ້າເຖິງ SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ສະພາບອາກາດ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ຄຸນນະພາບອາກາດ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ຂໍ້ມູນການສົ່ງສັນຍານ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ເລືອກຮູບໂປຣໄຟລ໌"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ແປ້ນພິມພາຍນອກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index a7aba78..946f69c 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Naudoti sistemos pasirink. (numatytasis)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Naudoti sistemos pasirink. (numatytasis)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Naudoti sistemos pasirink. (numatytasis)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8aaec47..e38889b 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Įvesties įrenginys"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prieiga prie interneto"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktų ir skambučių istorijos bendrinimas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Naudoti kontaktams ir skambučių istorijai bendrinti"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneto ryšio bendrinimas"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksto pranešimai"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Orai"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Oro kokybė"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Perdav. informacija"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Namų sist. valdikl."</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Pasirinkite profilio nuotrauką"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Numatytojo naudotojo piktograma"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizinė klaviatūra"</string>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 840d794..f4ae452 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sistēmas atlases izmantošana (nokl.)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sistēmas atlases izmantošana (nokl.)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sistēmas atlases izmantošana (nokl.)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index fcfb9b89..d899ada 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ievades ierīce"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneta piekļuve"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktpersonu un zvanu vēst. kopīgošana"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Paredzēts kontaktpersonu un zvanu vēstures kopīgošanai"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneta savienojuma koplietošana"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Īsziņas"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Laikapstākļi"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Gaisa kvalitāte"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Apraides informācija"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profila attēla izvēle"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Noklusējuma lietotāja ikona"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziskā tastatūra"</string>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 2f22165..9c46f21 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Користи избор на системот (стандардно)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Користи избор на системот (стандардно)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Користи избор на системот (стандардно)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 144de00..c8033e3 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Влезен уред"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Пристап до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Споделување контакти и историја на повици"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користење на споделувањето контакти и историја на повици"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделување конекција на интернет"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстуални пораки"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Пристап до SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Временска прогноза"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет на воздух"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Инфо за улогите"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за домот"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Изберете профилна слика"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Икона за стандарден корисник"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 3aa9472..4715e2a 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 59cd6bc..089f6af 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ഇൻപുട്ട് ഉപകരണം"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ഇന്റർനെറ്റ് ആക്സസ്"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"കോൺടാക്റ്റുകളും കോൾ ചരിത്രം പങ്കിടലും"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"കോൺടാക്റ്റുകളുടെയും കോൾ ചരിത്രം പങ്കിടലിന്റെയും ഉപയോഗം"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടൽ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"അക്ഷര സന്ദേശങ്ങൾ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"സിം ആക്സസ്"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"കാലാവസ്ഥ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"വായു നിലവാരം"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"കാസ്റ്റ് വിവരങ്ങൾ"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ഹോം കൺട്രോളുകൾ"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ഫിസിക്കൽ കീബോർഡ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index 9bd10a78..63fa53b 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
<item msgid="8003118270854840095">"44.1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 1bd6bea..cc35053 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Оруулах төхөөрөмж"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернэт хандалт"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Харилцагчид ба дуудлагын түүх хуваалцах"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Харилцагчид ба дуудлагын түүх хуваалцахад ашиглах"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернэт холболтыг хуваалцах"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Мессеж"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Хандалт"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Цаг агаар"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Агаарын чанар"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Дамжуулах мэдээлэл"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Гэрийн удирдлага"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Профайл зураг сонгох"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Биет гар"</string>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 45bc5b8..a54f990 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"सिस्टीम निवड वापरा (डीफॉल्ट)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c163219..1907d00 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिव्हाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट अॅक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"संपर्क आणि कॉल इतिहास शेअरिंग"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"संपर्क आणि कॉल इतिहास शेअरिंगसाठी वापरा"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन शेअररण"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"मजकूर मेसेज"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम अॅक्सेस"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"हवामान"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवेची गुणवत्ता"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसंबंधित माहिती"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो निवडा"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"डीफॉल्ट वापरकर्ता आयकन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"वास्तविक कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index d88ea76..b26ed23 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Lalai)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Lalai)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gunakan Pilihan Sistem (Lalai)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 55c8814..b3929c9 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Perkongsian kenalan & sejarah panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Digunakan untuk perkongsian kenalan dan sejarah panggilan"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualiti Udara"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maklumat Pelakon"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Pilih gambar profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna lalai"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Papan kekunci fizikal"</string>
diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml
index 857a6ae..ed95dfe 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
<item msgid="8003118270854840095">"၄၄.၁ kHz"</item>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 38b1fe8..f7a33a9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ထည့်သွင်းသော စက်"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"အဆက်အသွယ်၊ ယခင်ခေါ်ဆိုမှုများ မျှဝေခြင်း"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"အဆက်အသွယ်နှင့် ယခင်ခေါ်ဆိုမှုများ မျှဝေရန် သုံးသည်"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"အင်တာနက်ဆက်သွယ်မှု မျှဝေခြင်း"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"မိုဘိုင်းမက်ဆေ့ဂျ်များ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM အသုံးပြုခြင်း"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"မိုးလေဝသ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"လေထုအရည်အသွေး"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ကာစ် အချက်အလက်"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ပရိုဖိုင်ပုံ ရွေးပါ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ပကတိ ကီးဘုတ်"</string>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 0f6e1fc..317c2db 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Bruk systemvalg (standard)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Bruk systemvalg (standard)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Bruk systemvalg (standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index ef2111d..9b99631 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inndataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internett-tilgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling av kontakter og anropslogg"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Bruk for deling av kontakter og anropslogg"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling av internettilkobling"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstmeldinger"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vær"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformasjon"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Velg et profilbilde"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standard brukerikon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 611066e..b3c3ee2 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
<item msgid="8003118270854840095">"४४.१ kHz"</item>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 9fa7749..1534cba 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट पहुँच"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"कन्ट्याक्ट र कलको इतिहास सेयर गर्ने"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"कन्ट्याक्ट र कलको इतिहास सेयर गर्न प्रयोग गरियोस्"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM पहुँच"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"वायुको गुणस्तर"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसम्बन्धी जानकारी"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो छान्नुहोस्"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"प्रयोगकर्ताको डिफल्ट आइकन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"भौतिक किबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index e0690df..e809452 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Systeemselectie gebruiken (standaard)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Gebruik systeemselectie (standaard)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Systeemselectie gebruiken (standaard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f4cbfd5..ba11b46 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Bestandsoverdracht"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoerapparaat"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacten en gespreksgeschiedenis delen"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruiken om contacten en gespreksgeschiedenis te delen"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetverbinding delen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-berichten"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luchtkwaliteit"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformatie"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Bediening voor in huis"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Kies een profielfoto"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standaard gebruikersicoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiek toetsenbord"</string>
diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index dd25b3e..d42aeaf 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ସିଷ୍ଟମ୍ ଚୟନ ବ୍ୟବହାର କରନ୍ତୁ (ଡିଫଲ୍ଟ)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ଅଡିଓ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ଅଡିଓ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ସିଷ୍ଟମ୍ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ଅଡିଓ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ଅଡିଓ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ସିଷ୍ଟମ୍ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 76c3671..09fa59d 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍ପୁଟ୍ ଡିଭାଇସ୍"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟର୍ନେଟ୍ ଆକ୍ସେସ୍"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ୍"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ପାଣିପାଗ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ବାୟୁର ଗୁଣବତ୍ତା"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"କାଷ୍ଟ ସୂଚନା"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ଡିଫଲ୍ଟ ଉପଯୋଗକର୍ତ୍ତା ଆଇକନ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ଫିଜିକାଲ କୀବୋର୍ଡ"</string>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index 6ba2145..a3fae22 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index e34ece8..9df0fef 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ਇਨਪੁੱਟ ਡੀਵਾਈਸ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨਾ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ਸੰਪਰਕ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ ਸਾਂਝਾ ਕਰਨ ਲਈ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਸਾਂਝਾਕਰਨ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ਲਿਖਤ ਸੁਨੇਹੇ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ਸਿਮ ਪਹੁੰਚ"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ਮੌਸਮ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ਹਵਾ ਦੀ ਕੁਆਲਿਟੀ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ਕਾਸਟ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ਕੋਈ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਚੁਣੋ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਰਤੋਂਕਾਰ ਪ੍ਰਤੀਕ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ"</string>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 5f91d35..f0453d1 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Używaj wyboru systemu (domyślnie)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Używaj wyboru systemu (domyślnie)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Używaj wyboru systemu (domyślnie)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fe56c61..f4599fd 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Przesyłanie pliku"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Urządzenie wejściowe"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Dostęp do internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Udostępnianie kontaktów i historii połączeń"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Używaj w przypadku udostępniania kontaktów i historii połączeń"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Udostępnianie połączenia internetowego"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-y"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string>
@@ -228,7 +226,7 @@
<string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
<string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opcje programisty"</string>
- <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string>
+ <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje programisty"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Opcje programisty są niedostępne dla tego użytkownika"</string>
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Ustawienia VPN są niedostępne dla tego użytkownika"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Pogoda"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Jakość powietrza"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Obsada"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Wybierz zdjęcie profilowe"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona domyślnego użytkownika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Klawiatura fizyczna"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index c5a847f..1883ef3 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index aa66c6043..12507c5 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 3e7ee05..985bd51 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Utilizar seleção do sistema (predefinido)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Utilizar seleção do sistema (predefinido)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utilizar seleção do sistema (predefinido)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index cf76780..4988136 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência do ficheiro"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Part. histórico de chamadas e contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para partilha do histórico de chamadas e dos contactos"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partilha da ligação à internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteorologia"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ctr. domésticos"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolha uma imagem do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone do utilizador predefinido"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index c5a847f..1883ef3 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index aa66c6043..12507c5 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 454867e..f1e9986 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Folosiți selectarea sistemului (prestabilit)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Folosiți selectarea sistemului (prestabilit)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Folosiți selectarea sistemului (prestabilit)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 946f4b7..f49fcdd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Acces la agendă și istoricul apelurilor"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosiți pentru accesul la agendă și istoricul apelurilor"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calitatea aerului"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informații artiști"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Alegeți o fotografie de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Pictograma prestabilită a utilizatorului"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastatură fizică"</string>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 28b4695..b1211a5 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Выбор системы (по умолчанию)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Выбор системы (по умолчанию)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Аудиокодек: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Выбор системы (по умолчанию)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bcd83cf..38930a5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ к контактам и журналу звонков"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Использовать для доступа к контактам и журналу звонков"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Профиль PAN"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстовые сообщения"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество воздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Данные о трансляции"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Выберите фото профиля"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Значок пользователя по умолчанию"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физическая клавиатура"</string>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index c2dbf60..8386c1a 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්රව්යය"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්රව්යය"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්රව්යය"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්රව්යය"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 7f5eb49..fa0296f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ආදාන උපාංගය"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"අන්තර්ජාල ප්රවේශය"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය බෙදා ගැනීම සඳහා භාවිතා කරන්න"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"අන්තර්ජාල සම්බන්ධතා බෙදාගැනීම"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"පෙළ පණිවිඩ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ප්රවේශය"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"කාලගුණය"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"වායු ගුණත්වය"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"විකාශ තතු"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"පැතිකඩ පින්තූරයක් තේරීම"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"පෙරනිමි පරිශීලක නිරූපකය"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"භෞතික යතුරු පුවරුව"</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index b285888..370b23f 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Použiť voľbu systému (predvolené)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Použiť voľbu systému (predvolené)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Použiť voľbu systému (predvolené)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 31925ea..a7c69c8 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľanie kontaktov a histórie hovorov"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Počasie"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informácie o prenose"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládanie domácnosti"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Výber profilovej fotky"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Predvolená ikona používateľa"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnica"</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 8db3e99..6e33e38 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Uporabi sistemsko izbiro (privzeto)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Uporabi sistemsko izbiro (privzeto)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Uporabi sistemsko izbiro (privzeto)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 687ff38..7334cea 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vnosna naprava"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetni dostop"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje stikov in zgodovine klicev"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uporabite za deljenje stikov in zgodovine klicev."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internetne povezave"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sporočila SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kakovost zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"O zasedbi"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Nadzor doma"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Izbira profilne slike"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Privzeta ikona uporabnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizična tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index a82f716..8a6d853 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 7e96990..59359cdf 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Pajisja e hyrjes"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Qasje në internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ndarje: kontakte e historik telefonatash"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Përdor për ndarje kontaktesh e të historikut të telefonatave"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ndarja e lidhjes së internetit"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesazhet me tekst"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Moti"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Cilësia e ajrit"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Të dhënat e aktorëve"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Zgjidh një fotografi profili"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona e parazgjedhur e përdoruesit"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fizike"</string>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index 4cd83606..7e198bf 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Користи избор система (подразумевано)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Користи избор система (подразумевано)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Користи избор система (подразумевано)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2842223..eee3f89 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Улазни уређај"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Приступ Интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Дељење контаката и историје позива"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Користите за дељење контаката и историје позива"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Дељење интернет везе"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ови"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Време"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет ваздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Подаци о пребацивању"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Одаберите слику профила"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Подразумевана икона корисника"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index 1432ec2..f99a85b 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Använd systemval (standardinställning)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Använd systemval (standardinställning)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Använd systemval (standardinställning)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b0f8df5..1a8815c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Indataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetåtkomst"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Delning av kontakter och samtalshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Använd för delning av kontakter och samtalshistorik"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Delning av Internetanslutning"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Väder"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info om rollistan"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Välj en profilbild"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon för standardanvändare"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiskt tangentbord"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index cb74761..dab4279 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"ramani ya 13"</item>
<item msgid="8147982633566548515">"ramani ya 14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="8003118270854840095">"kHz 44.1"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 7e62d3f..5f1d141 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kushiriki anwani na rekodi ya simu zilizopigwa"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Tumia kushiriki anwani na rekodi ya simu zilizopigwa"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hali ya Hewa"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ubora wa Hewa"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maelezo ya Wahusika"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Chagua picha ya wasifu"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Aikoni chaguomsingi ya mtumiaji"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Kibodi halisi"</string>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 1f5380f..a0f1fa6 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index abab3fb..b8c37b0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"தொடர்புகள் & அழைப்புப் பதிவைப் பகிர்தல்"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"தொடர்புகளையும் அழைப்புப் பதிவையும் பகிர்வதற்குப் பயன்படுத்து"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"இணைய இணைப்பு பகிர்தல்"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"உரைச் செய்திகள்"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"சிம் அணுகல்"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"வானிலை"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"காற்றின் தரம்"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"அலைபரப்புத் தகவல்"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"சுயவிவரப் படத்தைத் தேர்வுசெய்யுங்கள்"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"இயல்புநிலைப் பயனர் ஐகான்"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"கீபோர்டு"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 513fb6e..18a2758 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a362f24..504c827 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్పుట్ పరికరం"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్లు"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"వాతావరణం"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"గాలి క్వాలిటీ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"కాస్ట్ సమాచారం"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"హోమ్ కంట్రోల్స్"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"ప్రొఫైల్ ఫోటోను ఎంచుకోండి"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ఆటోమేటిక్ సెట్టింగ్ యూజర్ చిహ్నం"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"భౌతిక కీబోర్డ్"</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 732124a..04a5f4d 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a4e0508..f33630d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"อุปกรณ์อินพุต"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"การเข้าถึงอินเทอร์เน็ต"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"การแชร์รายชื่อติดต่อและประวัติการโทร"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"ใช้สำหรับการแชร์รายชื่อติดต่อและประวัติการโทร"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"การแชร์การเชื่อมต่ออินเทอร์เน็ต"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ข้อความ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"การเข้าถึงซิม"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"สภาพอากาศ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"คุณภาพอากาศ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ข้อมูลแคสต์"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"เลือกรูปโปรไฟล์"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ไอคอนผู้ใช้เริ่มต้น"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"แป้นพิมพ์จริง"</string>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index bdd8706..59cb1f3 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Gamitin ang Pagpili ng System (Default)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Gamitin ang Pagpili ng System (Default)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gamitin ang Pagpili ng System (Default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index e28fd96..f95f0e50 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Device sa pag-input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Access sa internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts at pagbabahagi ng call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gamitin para sa contacts at pagbabahagi ng call history"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Pagbabahagi ng koneksyon sa internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mga Text Message"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Lagay ng Panahon"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kalidad ng Hangin"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Impormasyon ng Cast"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Pumili ng larawan sa profile"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icon ng default na user"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Pisikal na keyboard"</string>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 0d0c69a..5ed35fa 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sistem Seçimini Kullan (Varsayılan)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sistem Seçimini Kullan (Varsayılan)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sistem Seçimini Kullan (Varsayılan)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 05ec1cc..2fa2bd1 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Giriş cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternet erişimi"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kişi ve çağrı geçmişi paylaşımı"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kişi ve çağrı geçmişi paylaşımı için kullanın"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"İnternet bağlantısı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Kısa Mesajlar"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hava durumu"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Hava Kalitesi"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayın Bilgisi"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil fotoğrafı seçin"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Varsayılan kullanıcı simgesi"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziksel klavye"</string>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 49a4635..0410bd6 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Використовувати вибір системи (за умовчанням)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Використовувати вибір системи (за умовчанням)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Аудіо <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Використовувати вибір системи (за умовчанням)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 184d0c1..9be0855 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Надсилання контактів та історії викликів"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Використовуйте, щоб надсилати контакти й історію викликів"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ до SIM-карти"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якість повітря"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Акторський склад"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Виберіть зображення профілю"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Значок користувача за умовчанням"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Фізична клавіатура"</string>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index 5dc2123..d097458 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 419833a..8eb9a11 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ان پٹ آلہ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"انٹرنیٹ تک رسائی"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"رابطے اور کال کی سرگزشت کا اشتراک"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"رابطے اور کال کی سرگزشت کے اشتراک کے لیے استعمال کریں"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"انٹرنیٹ کنکشن کا اشتراک کرنا"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ٹیکسٹ پیغامات"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM رسائی"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"موسم"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ہوا کا معیار"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"کاسٹ کرنے کی معلومات"</string>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ہوم کنٹرولز"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"پروفائل کی تصویر منتخب کریں"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ڈیفالٹ صارف کا آئیکن"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"فزیکل کی بورڈ"</string>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 589c592..7d09027 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Tizim tanlovi (birlamchi)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audiokodeki"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Tizim tanlovi (birlamchi)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audiokodeki"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Tizim tanlovi (birlamchi)"</item>
<item msgid="8003118270854840095">"44.1 kGs"</item>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b1e22af..a5c8683 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl uzatish"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kiritish qurilmasi"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetga ulanish"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontakt va chaqiruvlar tarixiga kirish"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar va chaqiruvlar tarixiga kirish uchun foydalaning"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet aloqasi ulashmasi"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS xabarlari"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM kartaga kirish"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ob-havo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havo sifati"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Translatsiya axboroti"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil rasmini tanlash"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Foydalanuvchining standart belgisi"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tashqi klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 4cf8ff4..31867e2 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="2908219194098827570">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
- <item msgid="3517061573669307965">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 71bb08e..45a0465 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Thiết bị đầu vào"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Truy cập Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Chia sẻ danh bạ và nhật ký cuộc gọi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Dùng để chia sẻ danh bạ và nhật ký cuộc gọi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Chia sẻ kết nối internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tin nhắn văn bản"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Thời tiết"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Chất lượng không khí"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Thông tin về dàn nghệ sĩ"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Chọn một ảnh hồ sơ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Biểu tượng người dùng mặc định"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Bàn phím thực"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index af87f6f..973d7d0 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"使用系统选择(默认)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"使用系统选择(默认)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"使用系统选择(默认)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index f9e087b..2bc8a30 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"联系人信息和通话记录分享"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用于联系人信息和通话记录分享"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取权限"</string>
@@ -661,7 +659,9 @@
<string name="dream_complication_title_date" msgid="8661176085446135789">"日期"</string>
<string name="dream_complication_title_weather" msgid="598609151677172783">"天气"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空气质量"</string>
- <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投射信息"</string>
+ <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放信息"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"选择个人资料照片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"默认用户图标"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index 2099410..87f3825 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"使用系統選擇 (預設)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"使用系統選擇 (預設)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"使用系統選擇 (預設)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index cd93f71..114bc64 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互聯網連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享通訊錄及通話記錄"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享通訊錄及通話記錄"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"互聯網連線分享"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短訊"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣質素"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放資料"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人檔案相片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 24991e3..529287f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"系統自動選擇 (預設)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"系統自動選擇 (預設)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
- <item msgid="2553206901068987657">"LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"系統自動選擇 (預設)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 4f36742..a7ae213 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"網際網路連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"分享聯絡人和通話記錄"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"用於分享聯絡人和通話記錄"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"網際網路連線分享"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"簡訊"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取權"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣品質"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"演出者資訊"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人資料相片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 8732b26..59ead86 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"Imephu13"</item>
<item msgid="8147982633566548515">"Imephu14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
- <item msgid="4055460186095649420">"SBC"</item>
- <item msgid="720249083677397051">"I-AAC"</item>
- <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
- <item msgid="3825367753087348007">"I-LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
- <item msgid="9024885861221697796">"SBC"</item>
- <item msgid="4688890470703790013">"I-AAC"</item>
- <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
- <item msgid="2553206901068987657">"I-LDAC"</item>
- </string-array>
+ <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
+ <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
+ <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 88c41c5..e037d12 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Idivaysi yokufakwayo"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ukufinyelela i-Inthanethi"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ukwabelana ngoxhumana nabo nomlando wekholi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ukusetshenziswa kokwabelana ngoxhumana nabo nomlando wekholi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ukwabelana ngoxhumano lwe-Inthanethi"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Imilayezo yombhalo"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Isimo sezulu"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ikhwalithi Yomoya"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Ulwazi Lokusakaza"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Khetha isithombe sephrofayela"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Isithonjana somsebenzisi sokuzenzakalelayo"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Ikhibhodi ephathekayo"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 29a1831..663e8e4 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -154,6 +154,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -164,6 +166,8 @@
<item>2</item>
<item>3</item>
<item>4</item>
+ <item>5</item>
+ <item>6</item>
</string-array>
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -174,6 +178,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a79cbb6..b879223 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1610,6 +1610,9 @@
<string name="dream_complication_title_cast_info">Cast Info</string>
<!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
<string name="dream_complication_title_home_controls">Home Controls</string>
+ <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_smartspace">Smartspace</string>
+
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index df19c67..1940986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,6 +43,8 @@
public class A2dpProfile implements LocalBluetoothProfile {
private static final String TAG = "A2dpProfile";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private Context mContext;
private BluetoothA2dp mService;
@@ -328,6 +330,12 @@
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
index = 5;
break;
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+ index = 6;
+ break;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ index = 7;
+ break;
}
if (index < 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index a46e232..2258617 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -92,7 +92,8 @@
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComplicationType {}
@@ -103,6 +104,7 @@
public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
public static final int COMPLICATION_TYPE_CAST_INFO = 5;
public static final int COMPLICATION_TYPE_HOME_CONTROLS = 6;
+ public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
private final Context mContext;
private final IDreamManager mDreamManager;
@@ -351,6 +353,9 @@
case COMPLICATION_TYPE_HOME_CONTROLS:
res = R.string.dream_complication_title_home_controls;
break;
+ case COMPLICATION_TYPE_SMARTSPACE:
+ res = R.string.dream_complication_title_smartspace;
+ break;
default:
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 1a08366..b416738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,6 +44,8 @@
private final Handler mReceiverHandler;
private final MobileTelephonyCallback mTelephonyCallback;
+ private boolean mListening = false;
+
/**
* MobileStatusTracker constructors
*
@@ -76,6 +78,7 @@
* Config the MobileStatusTracker to start or stop monitoring platform signals.
*/
public void setListening(boolean listening) {
+ mListening = listening;
if (listening) {
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
} else {
@@ -83,6 +86,10 @@
}
}
+ public boolean isListening() {
+ return mListening;
+ }
+
private void updateDataSim() {
int activeDataSubId = mDefaults.getActiveDataSubId();
if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 2f36ab9..8f9ced6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -23,7 +23,6 @@
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
-import android.service.dreams.IDreamManager
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
@@ -54,7 +53,7 @@
class DialogLaunchAnimator
@JvmOverloads
constructor(
- private val dreamManager: IDreamManager,
+ private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
private val isForTesting: Boolean = false
@@ -126,7 +125,7 @@
val animatedDialog =
AnimatedDialog(
launchAnimator,
- dreamManager,
+ callback,
interactionJankMonitor,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
@@ -194,8 +193,12 @@
val dialog = animatedDialog.dialog
- // Don't animate if the dialog is not showing.
- if (!dialog.isShowing) {
+ // Don't animate if the dialog is not showing or if we are locked and going to show the
+ // bouncer.
+ if (
+ !dialog.isShowing ||
+ (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+ ) {
return null
}
@@ -285,6 +288,23 @@
?.let { it.touchSurface = it.prepareForStackDismiss() }
dialog.dismiss()
}
+
+ interface Callback {
+ /** Whether the device is currently in dreaming (screensaver) mode. */
+ fun isDreaming(): Boolean
+
+ /**
+ * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the
+ * keyguard can be dismissed.
+ */
+ fun isUnlocked(): Boolean
+
+ /**
+ * Whether we are going to show alternate authentication (like UDFPS) instead of the
+ * traditional bouncer when unlocking the device.
+ */
+ fun isShowingAlternateAuthOnUnlock(): Boolean
+ }
}
/**
@@ -296,7 +316,7 @@
private class AnimatedDialog(
private val launchAnimator: LaunchAnimator,
- private val dreamManager: IDreamManager,
+ private val callback: DialogLaunchAnimator.Callback,
private val interactionJankMonitor: InteractionJankMonitor,
/** The view that triggered the dialog after being tapped. */
@@ -850,7 +870,7 @@
// If we are dreaming, the dialog was probably closed because of that so we don't animate
// into the touchSurface.
- if (dreamManager.isDreaming) {
+ if (callback.isDreaming()) {
return false
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index a8a526a..f7049cf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -48,9 +48,13 @@
val default: T
}
+/**
+ * Base class for most common boolean flags.
+ *
+ * See [UnreleasedFlag] and [ReleasedFlag] for useful implementations.
+ */
// Consider using the "parcelize" kotlin library.
-
-data class BooleanFlag @JvmOverloads constructor(
+abstract class BooleanFlag constructor(
override val id: Int,
override val default: Boolean = false,
override val teamfood: Boolean = false,
@@ -60,7 +64,7 @@
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<BooleanFlag> {
- override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
+ override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
}
}
@@ -80,12 +84,46 @@
}
}
+/**
+ * A Flag that is is false by default.
+ *
+ * It can be changed or overridden in debug builds but not in release builds.
+ */
+data class UnreleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, false, teamfood, overridden)
+
+/**
+ * A Flag that is is true by default.
+ *
+ * It can be changed or overridden in any build, meaning it can be turned off if needed.
+ */
+data class ReleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, true, teamfood, overridden)
+
+/**
+ * A Flag that reads its default values from a resource overlay instead of code.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class ResourceBooleanFlag @JvmOverloads constructor(
override val id: Int,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from DeviceConfig.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class DeviceConfigBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
@@ -94,6 +132,13 @@
override val teamfood: Boolean = false
) : DeviceConfigFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from System Properties.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class SysPropBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 2560284..74bd9c6 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -27,7 +27,8 @@
import javax.inject.Named
@Module(includes = [
- SettingsUtilModule::class
+ ServerFlagReaderModule::class,
+ SettingsUtilModule::class,
])
abstract class FlagsModule {
@Binds
@@ -46,4 +47,4 @@
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index ab9e01e..38b5c9a 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -19,8 +19,8 @@
import dagger.Binds
import dagger.Module
-@Module
+@Module(includes = [ServerFlagReaderModule::class])
abstract class FlagsModule {
@Binds
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 23195af..00f1c01 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,6 +33,7 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main;
@@ -208,7 +209,7 @@
hide();
if (mKeyguardCallback != null) {
mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
- /* bypassSecondaryLockScreen= */true);
+ /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index eb418ff..b8fcb10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -179,7 +179,7 @@
if (dismissKeyguard) {
mDismissing = true;
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
- getKeyguardSecurityCallback().dismiss(true, userId);
+ getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
}
} else {
if (isValidPassword) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index d32219a..db64f05 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -90,7 +90,7 @@
Log.i(TAG, "TrustAgent dismissed Keyguard.");
}
mSecurityCallback.dismiss(false /* authenticated */, userId,
- /* bypassSecondaryLockScreen */ false);
+ /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid);
} else {
mViewMediatorCallback.playTrustedSound();
}
@@ -102,9 +102,9 @@
@Override
public boolean dismiss(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
- authenticated, targetUserId, bypassSecondaryLockScreen);
+ authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode);
}
@Override
@@ -212,7 +212,8 @@
* @return True if the keyguard is done.
*/
public boolean dismiss(int targetUserId) {
- return mSecurityCallback.dismiss(false, targetUserId, false);
+ return mSecurityCallback.dismiss(false, targetUserId, false,
+ getCurrentSecurityMode());
}
/**
@@ -360,10 +361,10 @@
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
- != SecurityMode.None) {
+ SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode();
+ if (securityMode != SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
- false, KeyguardUpdateMonitor.getCurrentUser());
+ false, KeyguardUpdateMonitor.getCurrentUser(), securityMode);
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 98ac640..87300c3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -59,10 +59,11 @@
return false;
}
@Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
+ public void dismiss(boolean securityVerified, int targetUserId,
+ SecurityMode expectedSecurityMode) { }
@Override
public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { }
@Override
public void onUserInput() { }
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 58e0fb96..0ea1965 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -55,10 +55,11 @@
val faceAuthenticated: Boolean,
val faceDisabled: Boolean,
val goingToSleep: Boolean,
- val keyguardAwake: Boolean,
+ val keyguardAwakeExcludingBouncerShowing: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
+ val onlyFaceEnrolled: Boolean,
val primaryUser: Boolean,
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index b97d9671..9aa6f03 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -171,7 +171,7 @@
if (dismissKeyguard) {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
- getKeyguardSecurityCallback().dismiss(true, userId);
+ getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
}
} else {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index e384727..bc72f79 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -15,14 +15,17 @@
*/
package com.android.keyguard;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+
public interface KeyguardSecurityCallback {
/**
* Dismiss the given security screen.
* @param securityVerified true if the user correctly entered credentials for the given screen.
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+ * @param expectedSecurityMode The security mode that is invoking this dismiss.
*/
- void dismiss(boolean securityVerified, int targetUserId);
+ void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode);
/**
* Dismiss the given security screen.
@@ -30,8 +33,10 @@
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
* if any, during this dismissal.
+ * @param expectedSecurityMode The security mode that is invoking this dismiss.
*/
- void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen);
+ void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen,
+ SecurityMode expectedSecurityMode);
/**
* Manually report user activity to keep the device awake.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 8fb622a..f697e25 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -236,7 +236,12 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+ /**
+ * Potentially dismiss the current security screen, after validating that all device
+ * security has been unlocked. Otherwise show the next screen.
+ */
+ boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen,
+ SecurityMode expectedSecurityMode);
void userActivity();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0b3fe46..e1957c0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -156,14 +156,17 @@
}
@Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ public void dismiss(boolean authenticated, int targetId,
+ SecurityMode expectedSecurityMode) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false,
+ expectedSecurityMode);
}
@Override
public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
+ expectedSecurityMode);
}
public boolean isVerifyUnlockOnly() {
@@ -385,8 +388,13 @@
return mCurrentSecurityMode;
}
- public void dismiss(boolean authenticated, int targetUserId) {
- mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
+ /**
+ * Potentially dismiss the current security screen, after validating that all device
+ * security has been unlocked. Otherwise show the next screen.
+ */
+ public void dismiss(boolean authenticated, int targetUserId,
+ SecurityMode expectedSecurityMode) {
+ mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode);
}
public void reset() {
@@ -456,12 +464,21 @@
* completion.
* @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
* secondary lock screen requirement, if any.
+ * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
+ * indicates that no check should be done
* @return true if keyguard is done
*/
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ if (expectedSecurityMode != SecurityMode.Invalid
+ && expectedSecurityMode != getCurrentSecurityMode()) {
+ Log.w(TAG, "Attempted to invoke showNextSecurityScreenOrFinish with securityMode "
+ + expectedSecurityMode + ", but current mode is " + getCurrentSecurityMode());
+ return false;
+ }
+
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 2f6fa14..2a2e9bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -142,7 +142,9 @@
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
- if (entry.length() < 4) {
+ // A SIM PIN is 4 to 8 decimal digits according to
+ // GSM 02.17 version 5.0.1, Section 5.6 PIN Management
+ if ((entry.length() < 4) || (entry.length() > 8)) {
// otherwise, display a message to the user, and don't submit.
mMessageAreaController.setMessage(
com.android.systemui.R.string.kg_invalid_sim_pin_hint);
@@ -170,7 +172,8 @@
mRemainingAttempts = -1;
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
+ true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPin);
} else {
mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 47aa43b..203f9b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -69,7 +69,8 @@
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPuk);
} else {
resetState();
}
@@ -278,7 +279,8 @@
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
+ true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPuk);
} else {
mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 89d6fb5..acbea1b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -29,7 +29,7 @@
/**
* Translates items away/towards the hinge when the device is opened/closed. This is controlled by
- * the set of ids, which also dictact which direction to move and when, via a filter function.
+ * the set of ids, which also dictate which direction to move and when, via a filter function.
*/
@SysUIUnfoldScope
class KeyguardUnfoldTransition
@@ -55,7 +55,9 @@
ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
ViewIdToTranslate(
R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
+ ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever),
+ ViewIdToTranslate(R.id.start_button, LEFT, filterNever),
+ ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)),
progressProvider = unfoldProgressProvider)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 489b4be..8b5e3c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -213,7 +213,8 @@
* If no cancel signal has been received after this amount of time, set the biometric running
* state to stopped to allow Keyguard to retry authentication.
*/
- private static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
+ @VisibleForTesting
+ protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
@@ -316,10 +317,15 @@
private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
private static final int HAL_ERROR_RETRY_MAX = 20;
- private final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
+ @VisibleForTesting
+ protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
+ @VisibleForTesting
+ protected Handler getHandler() {
+ return mHandler;
+ }
private final Handler mHandler;
private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
@@ -707,6 +713,11 @@
private void handleFingerprintAuthFailed() {
Assert.isMainThread();
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthFailed()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -737,6 +748,11 @@
private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthenticated()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
try {
final int userId;
try {
@@ -2458,8 +2474,11 @@
}
final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
- final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
- && !statusBarShadeLocked;
+ // mKeyguardIsVisible is true even when the bouncer is shown, we don't want to run face auth
+ // on bouncer if both fp and fingerprint are enrolled.
+ final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible
+ && mDeviceInteractive && !mGoingToSleep
+ && !statusBarShadeLocked && !mBouncerFullyShown;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
@@ -2499,14 +2518,15 @@
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
+ final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- (mBouncerFullyShown && !mGoingToSleep
+ ((mBouncerFullyShown && !mGoingToSleep && onlyFaceEnrolled)
|| mAuthInterruptActive
|| mOccludingAppRequestingFace
- || awakeKeyguard
+ || awakeKeyguardExcludingBouncerShowing
|| shouldListenForFaceAssistant
|| mAuthController.isUdfpsFingerDown()
|| mUdfpsBouncerShowing)
@@ -2530,10 +2550,11 @@
faceAuthenticated,
faceDisabledForUser,
mGoingToSleep,
- awakeKeyguard,
+ awakeKeyguardExcludingBouncerShowing,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
+ onlyFaceEnrolled,
mIsPrimaryUser,
strongAuthAllowsScanning,
mSecureCameraLaunched,
@@ -2543,6 +2564,11 @@
return shouldListen;
}
+ private boolean isOnlyFaceEnrolled() {
+ return isFaceAuthEnabledForUser(getCurrentUser())
+ && !isUnlockWithFingerprintPossible(getCurrentUser());
+ }
+
private void maybeLogListenerModelData(KeyguardListenModel model) {
mLogger.logKeyguardListenerModel(model);
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
deleted file mode 100644
index 0db5bef..0000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.FalsingPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked
- * the data containing these events is saved to a file. This data is collected
- * to analyze how a human interaction looks like.
- *
- * A session starts when the screen is turned on.
- * A session ends when the screen is turned off or user unlocks the phone.
- */
-public class DataCollector implements SensorEventListener {
- private static final String TAG = "DataCollector";
- private static final String COLLECTOR_ENABLE = "data_collector_enable";
- private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
- private static final String ALLOW_REJECTED_TOUCH_REPORTS =
- "data_collector_allow_rejected_touch_reports";
- private static final String DISABLE_UNLOCKING_FOR_FALSING_COLLECTION =
- "data_collector_disable_unlocking";
-
- private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
- public static final boolean DEBUG = false;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Context mContext;
-
- // Err on the side of caution, so logging is not started after a crash even tough the screen
- // is off.
- private SensorLoggerSession mCurrentSession = null;
-
- private boolean mEnableCollector = false;
- private boolean mCollectBadTouches = false;
- private boolean mCornerSwiping = false;
- private boolean mTrackingStarted = false;
- private boolean mAllowReportRejectedTouch = false;
- private boolean mDisableUnlocking = false;
-
- private static DataCollector sInstance = null;
-
- private FalsingPlugin mFalsingPlugin = null;
-
- protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- updateConfiguration();
- }
- };
-
- private final PluginListener mPluginListener = new PluginListener<FalsingPlugin>() {
- public void onPluginConnected(FalsingPlugin plugin, Context context) {
- mFalsingPlugin = plugin;
- }
-
- public void onPluginDisconnected(FalsingPlugin plugin) {
- mFalsingPlugin = null;
- }
- };
-
- private DataCollector(Context context) {
- mContext = context;
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(COLLECTOR_ENABLE), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(COLLECT_BAD_TOUCHES), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(ALLOW_REJECTED_TOUCH_REPORTS), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(DISABLE_UNLOCKING_FOR_FALSING_COLLECTION), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- updateConfiguration();
-
- Dependency.get(PluginManager.class).addPluginListener(mPluginListener, FalsingPlugin.class);
- }
-
- public static DataCollector getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new DataCollector(context);
- }
- return sInstance;
- }
-
- private void updateConfiguration() {
- mEnableCollector = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- COLLECTOR_ENABLE, 0);
- mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- COLLECT_BAD_TOUCHES, 0);
- mAllowReportRejectedTouch = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- ALLOW_REJECTED_TOUCH_REPORTS, 0);
- mDisableUnlocking = mEnableCollector && Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- DISABLE_UNLOCKING_FOR_FALSING_COLLECTION, 0);
- }
-
- private boolean sessionEntrypoint() {
- if (isEnabled() && mCurrentSession == null) {
- onSessionStart();
- return true;
- }
- return false;
- }
-
- private void sessionExitpoint(int result) {
- if (mCurrentSession != null) {
- onSessionEnd(result);
- }
- }
-
- private void onSessionStart() {
- mCornerSwiping = false;
- mTrackingStarted = false;
- mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime());
- }
-
- private void onSessionEnd(int result) {
- SensorLoggerSession session = mCurrentSession;
- mCurrentSession = null;
-
- if (mEnableCollector || mDisableUnlocking) {
- session.end(System.currentTimeMillis(), result);
- queueSession(session);
- }
- }
-
- public Uri reportRejectedTouch() {
- if (mCurrentSession == null) {
- Toast.makeText(mContext, "Generating rejected touch report failed: session timed out.",
- Toast.LENGTH_LONG).show();
- return null;
- }
- SensorLoggerSession currentSession = mCurrentSession;
-
- currentSession.setType(Session.REJECTED_TOUCH_REPORT);
- currentSession.end(System.currentTimeMillis(), Session.SUCCESS);
- Session proto = currentSession.toProto();
-
- byte[] b = Session.toByteArray(proto);
- File dir = new File(mContext.getExternalCacheDir(), "rejected_touch_reports");
- dir.mkdir();
- File touch = new File(dir, "rejected_touch_report_" + System.currentTimeMillis());
-
- try {
- new FileOutputStream(touch).write(b);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- return Uri.fromFile(touch);
- }
-
- private void queueSession(final SensorLoggerSession currentSession) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- byte[] b = Session.toByteArray(currentSession.toProto());
-
- if (mFalsingPlugin != null) {
- mFalsingPlugin.dataCollected(currentSession.getResult() == Session.SUCCESS, b);
- } else {
- String dir = mContext.getFilesDir().getAbsolutePath();
- if (currentSession.getResult() != Session.SUCCESS) {
- if (!mDisableUnlocking && !mCollectBadTouches) {
- return;
- }
- dir += "/bad_touches";
- } else if (!mDisableUnlocking) {
- dir += "/good_touches";
- }
-
- File file = new File(dir);
- file.mkdir();
- File touch = new File(file, "trace_" + System.currentTimeMillis());
- try {
- new FileOutputStream(touch).write(b);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- });
- }
-
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- if (isEnabled() && mCurrentSession != null) {
- mCurrentSession.addSensorEvent(event, System.nanoTime());
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- /**
- * @return true if data is being collected - either for data gathering or creating a
- * rejected touch report.
- */
- public boolean isEnabled() {
- return mEnableCollector || mAllowReportRejectedTouch || mDisableUnlocking;
- }
-
- public boolean isUnlockingDisabled() {
- return mDisableUnlocking;
- }
- /**
- * @return true if the full data set for data gathering should be collected - including
- * extensive sensor data, which is is not normally included with rejected touch reports.
- */
- public boolean isEnabledFull() {
- return mEnableCollector;
- }
-
- public void onScreenTurningOn() {
- if (sessionEntrypoint()) {
- if (DEBUG) {
- Log.d(TAG, "onScreenTurningOn");
- }
- addEvent(PhoneEvent.ON_SCREEN_ON);
- }
- }
-
- public void onScreenOnFromTouch() {
- if (sessionEntrypoint()) {
- if (DEBUG) {
- Log.d(TAG, "onScreenOnFromTouch");
- }
- addEvent(PhoneEvent.ON_SCREEN_ON_FROM_TOUCH);
- }
- }
-
- public void onScreenOff() {
- if (DEBUG) {
- Log.d(TAG, "onScreenOff");
- }
- addEvent(PhoneEvent.ON_SCREEN_OFF);
- sessionExitpoint(Session.FAILURE);
- }
-
- public void onSucccessfulUnlock() {
- if (DEBUG) {
- Log.d(TAG, "onSuccessfulUnlock");
- }
- addEvent(PhoneEvent.ON_SUCCESSFUL_UNLOCK);
- sessionExitpoint(Session.SUCCESS);
- }
-
- public void onBouncerShown() {
- if (DEBUG) {
- Log.d(TAG, "onBouncerShown");
- }
- addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
- }
-
- public void onBouncerHidden() {
- if (DEBUG) {
- Log.d(TAG, "onBouncerHidden");
- }
- addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
- }
-
- public void onQsDown() {
- if (DEBUG) {
- Log.d(TAG, "onQsDown");
- }
- addEvent(PhoneEvent.ON_QS_DOWN);
- }
-
- public void setQsExpanded(boolean expanded) {
- if (DEBUG) {
- Log.d(TAG, "setQsExpanded = " + expanded);
- }
- if (expanded) {
- addEvent(PhoneEvent.SET_QS_EXPANDED_TRUE);
- } else {
- addEvent(PhoneEvent.SET_QS_EXPANDED_FALSE);
- }
- }
-
- public void onTrackingStarted() {
- if (DEBUG) {
- Log.d(TAG, "onTrackingStarted");
- }
- mTrackingStarted = true;
- addEvent(PhoneEvent.ON_TRACKING_STARTED);
- }
-
- public void onTrackingStopped() {
- if (mTrackingStarted) {
- if (DEBUG) {
- Log.d(TAG, "onTrackingStopped");
- }
- mTrackingStarted = false;
- addEvent(PhoneEvent.ON_TRACKING_STOPPED);
- }
- }
-
- public void onNotificationActive() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationActive");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_ACTIVE);
- }
-
-
- public void onNotificationDoubleTap() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationDoubleTap");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_DOUBLE_TAP);
- }
-
- public void setNotificationExpanded() {
- if (DEBUG) {
- Log.d(TAG, "setNotificationExpanded");
- }
- addEvent(PhoneEvent.SET_NOTIFICATION_EXPANDED);
- }
-
- public void onNotificatonStartDraggingDown() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStartDraggingDown");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_START_DRAGGING_DOWN);
- }
-
- public void onStartExpandingFromPulse() {
- if (DEBUG) {
- Log.d(TAG, "onStartExpandingFromPulse");
- }
- // TODO: maybe add event
- }
-
- public void onExpansionFromPulseStopped() {
- if (DEBUG) {
- Log.d(TAG, "onExpansionFromPulseStopped");
- }
- // TODO: maybe add event
- }
-
- public void onNotificatonStopDraggingDown() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStopDraggingDown");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DRAGGING_DOWN);
- }
-
- public void onNotificationDismissed() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationDismissed");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_DISMISSED);
- }
-
- public void onNotificatonStartDismissing() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStartDismissing");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_START_DISMISSING);
- }
-
- public void onNotificatonStopDismissing() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStopDismissing");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DISMISSING);
- }
-
- public void onCameraOn() {
- if (DEBUG) {
- Log.d(TAG, "onCameraOn");
- }
- addEvent(PhoneEvent.ON_CAMERA_ON);
- }
-
- public void onLeftAffordanceOn() {
- if (DEBUG) {
- Log.d(TAG, "onLeftAffordanceOn");
- }
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_ON);
- }
-
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- if (DEBUG) {
- Log.d(TAG, "onAffordanceSwipingStarted");
- }
- mCornerSwiping = true;
- if (rightCorner) {
- addEvent(PhoneEvent.ON_RIGHT_AFFORDANCE_SWIPING_STARTED);
- } else {
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_SWIPING_STARTED);
- }
- }
-
- public void onAffordanceSwipingAborted() {
- if (mCornerSwiping) {
- if (DEBUG) {
- Log.d(TAG, "onAffordanceSwipingAborted");
- }
- mCornerSwiping = false;
- addEvent(PhoneEvent.ON_AFFORDANCE_SWIPING_ABORTED);
- }
- }
-
- public void onUnlockHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onUnlockHintStarted");
- }
- addEvent(PhoneEvent.ON_UNLOCK_HINT_STARTED);
- }
-
- public void onCameraHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onCameraHintStarted");
- }
- addEvent(PhoneEvent.ON_CAMERA_HINT_STARTED);
- }
-
- public void onLeftAffordanceHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onLeftAffordanceHintStarted");
- }
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED);
- }
-
- public void onTouchEvent(MotionEvent event, int width, int height) {
- if (mCurrentSession != null) {
- if (DEBUG) {
- Log.v(TAG, "onTouchEvent(ev.action="
- + MotionEvent.actionToString(event.getAction()) + ")");
- }
- mCurrentSession.addMotionEvent(event);
- mCurrentSession.setTouchArea(width, height);
- }
- }
-
- private void addEvent(int eventType) {
- if (isEnabled() && mCurrentSession != null) {
- mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
- }
- }
-
- public boolean isReportingEnabled() {
- return mAllowReportRejectedTouch;
- }
-
- public void onFalsingSessionStarted() {
- sessionEntrypoint();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
deleted file mode 100644
index d294012..0000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent;
-
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-/**
- * Collects touch, sensor and phone events and converts the data to
- * TouchAnalyticsProto.Session.
- */
-public class SensorLoggerSession {
- private static final String TAG = "SensorLoggerSession";
-
- private final long mStartTimestampMillis;
- private final long mStartSystemTimeNanos;
-
- private long mEndTimestampMillis;
- private int mType;
-
- private ArrayList<TouchEvent> mMotionEvents = new ArrayList<>();
- private ArrayList<SensorEvent> mSensorEvents = new ArrayList<>();
- private ArrayList<PhoneEvent> mPhoneEvents = new ArrayList<>();
- private int mTouchAreaHeight;
- private int mTouchAreaWidth;
- private int mResult = Session.UNKNOWN;
-
- public SensorLoggerSession(long startTimestampMillis, long startSystemTimeNanos) {
- mStartTimestampMillis = startTimestampMillis;
- mStartSystemTimeNanos = startSystemTimeNanos;
- mType = Session.REAL;
- }
-
- public void setType(int type) {
- mType = type;
- }
-
- public void end(long endTimestampMillis, int result) {
- mResult = result;
- mEndTimestampMillis = endTimestampMillis;
-
- if (DataCollector.DEBUG) {
- Log.d(TAG, "Ending session result=" + result + " it lasted for " +
- (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s");
- }
- }
-
- public void addMotionEvent(MotionEvent motionEvent) {
- TouchEvent event = motionEventToProto(motionEvent);
- mMotionEvents.add(event);
- }
-
- public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) {
- SensorEvent event = sensorEventToProto(eventOrig, systemTimeNanos);
- mSensorEvents.add(event);
- }
-
- public void addPhoneEvent(int eventType, long systemTimeNanos) {
- PhoneEvent event = phoneEventToProto(eventType, systemTimeNanos);
- mPhoneEvents.add(event);
- }
-
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("Session{");
- sb.append("mStartTimestampMillis=").append(mStartTimestampMillis);
- sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos);
- sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis);
- sb.append(", mResult=").append(mResult);
- sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight);
- sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth);
- sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]");
- sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]");
- sb.append(", mPhoneEvents=[size=").append(mPhoneEvents.size()).append("]");
- sb.append('}');
- return sb.toString();
- }
-
- public Session toProto() {
- Session proto = new Session();
- proto.startTimestampMillis = mStartTimestampMillis;
- proto.durationMillis = mEndTimestampMillis - mStartTimestampMillis;
- proto.build = Build.FINGERPRINT;
- proto.deviceId = Build.DEVICE;
- proto.result = mResult;
- proto.type = mType;
- proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents);
- proto.touchEvents = mMotionEvents.toArray(proto.touchEvents);
- proto.phoneEvents = mPhoneEvents.toArray(proto.phoneEvents);
- proto.touchAreaWidth = mTouchAreaWidth;
- proto.touchAreaHeight = mTouchAreaHeight;
- return proto;
- }
-
- private PhoneEvent phoneEventToProto(int eventType, long sysTimeNanos) {
- PhoneEvent proto = new PhoneEvent();
- proto.type = eventType;
- proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
- return proto;
- }
-
- private SensorEvent sensorEventToProto(android.hardware.SensorEvent ev, long sysTimeNanos) {
- SensorEvent proto = new SensorEvent();
- proto.type = ev.sensor.getType();
- proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
- proto.timestamp = ev.timestamp;
- proto.values = ev.values.clone();
- return proto;
- }
-
- private TouchEvent motionEventToProto(MotionEvent ev) {
- int count = ev.getPointerCount();
- TouchEvent proto = new TouchEvent();
- proto.timeOffsetNanos = ev.getEventTimeNano() - mStartSystemTimeNanos;
- proto.action = ev.getActionMasked();
- proto.actionIndex = ev.getActionIndex();
- proto.pointers = new TouchEvent.Pointer[count];
- for (int i = 0; i < count; i++) {
- TouchEvent.Pointer p = new TouchEvent.Pointer();
- p.x = ev.getX(i);
- p.y = ev.getY(i);
- p.size = ev.getSize(i);
- p.pressure = ev.getPressure(i);
- p.id = ev.getPointerId(i);
- proto.pointers[i] = p;
- }
- return proto;
- }
-
- public void setTouchArea(int width, int height) {
- mTouchAreaWidth = width;
- mTouchAreaHeight = height;
- }
-
- public int getResult() {
- return mResult;
- }
-
- public long getStartTimestampMillis() {
- return mStartTimestampMillis;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 835025b..e82d0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -16,8 +16,6 @@
package com.android.systemui.charging;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -32,13 +30,14 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.ripple.RippleShader.RippleShape;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
* @hide
*/
public class WirelessChargingAnimation {
-
+ public static final int UNKNOWN_BATTERY_LEVEL = -1;
public static final long DURATION = 1500;
private static final String TAG = "WirelessChargingView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -58,11 +57,12 @@
* before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
* @hide
*/
- public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
+ private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
- UiEventLogger uiEventLogger) {
+ RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
- transmittingBatteryLevel, batteryLevel, callback, isDozing, uiEventLogger);
+ transmittingBatteryLevel, batteryLevel, callback, isDozing,
+ rippleShape, uiEventLogger);
}
/**
@@ -72,9 +72,10 @@
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
@Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
- Callback callback, boolean isDozing, UiEventLogger uiEventLogger) {
+ Callback callback, boolean isDozing, RippleShape rippleShape,
+ UiEventLogger uiEventLogger) {
return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
- batteryLevel, callback, isDozing, uiEventLogger);
+ batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
}
/**
@@ -82,9 +83,10 @@
* battery level without charging number shown.
*/
public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
- @NonNull Context context, UiEventLogger uiEventLogger) {
+ @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
return makeWirelessChargingAnimation(context, null,
- UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false, uiEventLogger);
+ UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
+ rippleShape, uiEventLogger);
}
/**
@@ -121,10 +123,10 @@
public WirelessChargingView(Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback,
- boolean isDozing, UiEventLogger uiEventLogger) {
+ boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCallback = callback;
mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
- isDozing);
+ isDozing, rippleShape);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
mUiEventLogger = uiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 65400c2..47ea27f 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -33,7 +33,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.ripple.RippleShader;
+import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;
@@ -41,37 +41,36 @@
/**
* @hide
*/
-public class WirelessChargingLayout extends FrameLayout {
- public static final int UNKNOWN_BATTERY_LEVEL = -1;
+final class WirelessChargingLayout extends FrameLayout {
private static final long RIPPLE_ANIMATION_DURATION = 1500;
private static final int SCRIM_COLOR = 0x4C000000;
private static final int SCRIM_FADE_DURATION = 300;
private RippleView mRippleView;
- public WirelessChargingLayout(Context context) {
+ WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
+ boolean isDozing, RippleShape rippleShape) {
super(context);
- init(context, null, false);
+ init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape);
}
- public WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
- boolean isDozing) {
+ private WirelessChargingLayout(Context context) {
super(context);
- init(context, null, transmittingBatteryLevel, batteryLevel, isDozing);
+ init(context, null, /* isDozing= */ false, RippleShape.CIRCLE);
}
- public WirelessChargingLayout(Context context, AttributeSet attrs) {
+ private WirelessChargingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
- init(context, attrs, false);
+ init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE);
}
- private void init(Context c, AttributeSet attrs, boolean isDozing) {
- init(c, attrs, -1, -1, false);
+ private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape) {
+ init(c, attrs, -1, -1, isDozing, rippleShape);
}
private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
- int batteryLevel, boolean isDozing) {
+ int batteryLevel, boolean isDozing, RippleShape rippleShape) {
final boolean showTransmittingBatteryLevel =
- (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
+ (transmittingBatteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL);
// set style based on background
int style = R.style.ChargingAnim_WallpaperBackground;
@@ -84,7 +83,7 @@
// amount of battery:
final TextView percentage = findViewById(R.id.wireless_charging_percentage);
- if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
+ if (batteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL) {
percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
percentage.setAlpha(0);
}
@@ -138,8 +137,7 @@
animatorSetScrim.start();
mRippleView = findViewById(R.id.wireless_charging_ripple);
- // TODO: Make rounded box shape if the device is tablet.
- mRippleView.setupShader(RippleShader.RippleShape.CIRCLE);
+ mRippleView.setupShader(rippleShape);
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
@@ -233,8 +231,12 @@
int width = getMeasuredWidth();
int height = getMeasuredHeight();
mRippleView.setCenter(width * 0.5f, height * 0.5f);
- float maxSize = Math.max(width, height);
- mRippleView.setMaxSize(maxSize, maxSize);
+ if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
+ mRippleView.setMaxSize(width * 1.5f, height * 1.5f);
+ } else {
+ float maxSize = Math.max(width, height);
+ mRippleView.setMaxSize(maxSize, maxSize);
+ }
mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
android.R.attr.colorAccent).getDefaultColor());
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e16ac08..c21e36a 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -97,6 +97,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -132,7 +133,7 @@
private static final int FONT_SEARCH_STEP_PX = 4;
private final Context mContext;
- private final UiEventLogger mUiEventLogger;
+ private final ClipboardLogger mClipboardLogger;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DisplayManager mDisplayManager;
private final DisplayMetrics mDisplayMetrics;
@@ -181,7 +182,7 @@
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
- mUiEventLogger = uiEventLogger;
+ mClipboardLogger = new ClipboardLogger(uiEventLogger);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
@@ -231,7 +232,7 @@
@Override
public void onSwipeDismissInitiated(Animator animator) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
mExitAnimator = animator;
}
@@ -249,7 +250,7 @@
});
mDismissButton.setOnClickListener(view -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
animateOut();
});
@@ -285,7 +286,8 @@
int newDisplayId) {
if (mContext.getResources().getConfiguration().orientation
!= mOrientation) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(
+ CLIPBOARD_OVERLAY_DISMISSED_OTHER);
hideImmediate();
}
}
@@ -300,7 +302,7 @@
});
mTimeoutHandler.setOnTimeoutRunnable(() -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TIMED_OUT);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
animateOut();
});
@@ -308,7 +310,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -320,7 +322,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (SCREENSHOT_ACTION.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -390,7 +392,7 @@
mContext.getString(R.string.clipboard_send_nearby_description));
mRemoteCopyChip.setVisibility(View.VISIBLE);
mRemoteCopyChip.setOnClickListener((v) -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
mContext.startActivity(remoteCopyIntent);
animateOut();
});
@@ -450,7 +452,7 @@
chip.setContentDescription(action.getTitle());
chip.setIcon(action.getIcon(), false);
chip.setPendingIntent(action.getActionIntent(), () -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
animateOut();
});
chip.setAlpha(1);
@@ -486,7 +488,7 @@
touchRegion.op(tmpRect, Region.Op.UNION);
if (!touchRegion.contains(
(int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
animateOut();
}
}
@@ -497,7 +499,7 @@
}
private void editImage(Uri uri) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
String editorPackage = mContext.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -512,7 +514,7 @@
}
private void editText() {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
Intent editIntent = new Intent(mContext, EditTextActivity.class);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivity(editIntent);
@@ -520,13 +522,15 @@
}
private void shareContent(ClipData clip) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
shareIntent.setDataAndType(
clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
- shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
+ if (clip.getItemAt(0).getUri() != null) {
+ shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
Intent chooserIntent = Intent.createChooser(shareIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -864,6 +868,7 @@
mRemoteCopyChip.setVisibility(View.GONE);
resetActionChips();
mTimeoutHandler.cancelTimeout();
+ mClipboardLogger.reset();
}
@MainThread
@@ -969,4 +974,24 @@
mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
+
+ static class ClipboardLogger {
+ private final UiEventLogger mUiEventLogger;
+ private boolean mGuarded = false;
+
+ ClipboardLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ }
+
+ void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
+ if (!mGuarded) {
+ mGuarded = true;
+ mUiEventLogger.log(event);
+ }
+ }
+
+ void reset() {
+ mGuarded = false;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index de7bf28..55c1806 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -215,16 +215,15 @@
final boolean cameraBlocked = mSensorPrivacyController
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
@DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
- if (micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
- } else if (!micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
- } else if (micBlocked && !cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
- }
- if (iconType != Resources.ID_NULL) {
- showIcon(iconType, true);
- }
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+ !micBlocked && cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+ micBlocked && !cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ micBlocked && cameraBlocked);
}
private String buildNotificationsContentDescription(int notificationCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 5457144..29bb2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -163,7 +163,8 @@
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
@interface ComplicationType {}
@@ -175,6 +176,7 @@
int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
int COMPLICATION_TYPE_HOME_CONTROLS = 1 << 5;
+ int COMPLICATION_TYPE_SMARTSPACE = 1 << 6;
/**
* The {@link Host} interface specifies a way a {@link Complication} to communicate with its
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index dcab90f..d5db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -21,6 +21,7 @@
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
@@ -51,6 +52,8 @@
return COMPLICATION_TYPE_CAST_INFO;
case DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS:
return COMPLICATION_TYPE_HOME_CONTROLS;
+ case DreamBackend.COMPLICATION_TYPE_SMARTSPACE:
+ return COMPLICATION_TYPE_SMARTSPACE;
default:
return COMPLICATION_TYPE_NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index ac6edba..567bdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -52,6 +52,11 @@
return mViewHolderProvider.get();
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_SMARTSPACE;
+ }
+
/**
* {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
* SystemUI.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 2cee2520..dfa3bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -23,7 +23,10 @@
*/
interface FeatureFlags : FlagListenable {
/** Returns a boolean value for the given flag. */
- fun isEnabled(flag: BooleanFlag): Boolean
+ fun isEnabled(flag: UnreleasedFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: ReleasedFlag): Boolean
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: ResourceBooleanFlag): Boolean
@@ -39,4 +42,4 @@
/** Returns a string value for the given flag. */
fun getString(flag: ResourceStringFlag): String
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index f804325..b4b8795 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -48,6 +48,8 @@
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -84,6 +86,7 @@
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -98,6 +101,7 @@
@Main Resources resources,
DumpManager dumpManager,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
CommandRegistry commandRegistry,
IStatusBarService barService) {
@@ -106,6 +110,7 @@
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mBarService = barService;
@@ -121,7 +126,16 @@
}
@Override
- public boolean isEnabled(@NonNull BooleanFlag flag) {
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
@@ -198,14 +212,17 @@
/** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readFlagValue(int id, boolean defaultValue) {
Boolean result = readBooleanFlagOverride(id);
- // Only check for teamfood if the default is false.
- if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
+ boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+
+ // Only check for teamfood if the default is false
+ // and there is no server override.
+ if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
return isEnabled(Flags.TEAMFOOD);
}
}
- return result == null ? defaultValue : result;
+ return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
}
private Boolean readBooleanFlagOverride(int id) {
@@ -410,36 +427,38 @@
*/
@Nullable
private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
- if (f instanceof BooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((BooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof ResourceBooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((ResourceBooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof DeviceConfigBooleanFlag) {
- return new BooleanFlag(
- f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood());
- }
- if (f instanceof SysPropBooleanFlag) {
+ boolean enabled;
+ boolean teamfood = f.getTeamfood();
+ boolean overridden;
+
+ if (f instanceof ReleasedFlag) {
+ enabled = isEnabled((ReleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof UnreleasedFlag) {
+ enabled = isEnabled((UnreleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof ResourceBooleanFlag) {
+ enabled = isEnabled((ResourceBooleanFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof DeviceConfigBooleanFlag) {
+ enabled = isEnabled((DeviceConfigBooleanFlag) f);
+ overridden = false;
+ } else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
- return new BooleanFlag(
- f.getId(),
- ((SysPropBooleanFlag) f).getDefault(),
- false,
- !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty());
+ enabled = isEnabled((SysPropBooleanFlag) f);
+ teamfood = false;
+ overridden = !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty();
+ } else {
+ // TODO: add support for other flag types.
+ Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
+ return null;
}
- // TODO: add support for other flag types.
- Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
- return null;
+ if (enabled) {
+ return new ReleasedFlag(f.getId(), teamfood, overridden);
+ } else {
+ return new UnreleasedFlag(f.getId(), teamfood, overridden);
+ }
}
};
@@ -540,8 +559,10 @@
}
private boolean isBooleanFlagEnabled(Flag<?> flag) {
- if (flag instanceof BooleanFlag) {
- return isEnabled((BooleanFlag) flag);
+ if (flag instanceof ReleasedFlag) {
+ return isEnabled((ReleasedFlag) flag);
+ } else if (flag instanceof UnreleasedFlag) {
+ return isEnabled((UnreleasedFlag) flag);
} else if (flag instanceof ResourceBooleanFlag) {
return isEnabled((ResourceBooleanFlag) flag);
} else if (flag instanceof SysPropFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 1492a2b..049b17d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -19,7 +19,6 @@
import static java.util.Objects.requireNonNull;
import android.content.res.Resources;
-import android.provider.DeviceConfig;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -31,6 +30,8 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.DeviceConfigProxy;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.Map;
@@ -47,18 +48,22 @@
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
+ private boolean mInited;
@Inject
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
DumpManager dumpManager) {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -69,8 +74,13 @@
public void removeListener(@NonNull Listener listener) {}
@Override
- public boolean isEnabled(BooleanFlag flag) {
- return flag.getDefault();
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return mServerFlagReader.readServerOverride(flag.getId(), true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3d40e67..6eb77bd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -32,7 +32,7 @@
*
* Flag Ids are integers.
* Ids must be unique. This is enforced in a unit test.
- * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related featurs with
+ * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related features with
* a comment. This is purely for organizational purposes.
*
* On public release builds, flags will always return their default value. There is no way to
@@ -41,30 +41,30 @@
* See {@link FeatureFlagsDebug} for instructions on flipping the flags via adb.
*/
public class Flags {
- public static final BooleanFlag TEAMFOOD = new BooleanFlag(1, false);
+ public static final UnreleasedFlag TEAMFOOD = new UnreleasedFlag(1);
/***************************************/
// 100 - notification
- public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
- new BooleanFlag(103, false);
+ public static final UnreleasedFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ new UnreleasedFlag(103);
- public static final BooleanFlag NSSL_DEBUG_LINES =
- new BooleanFlag(105, false);
+ public static final UnreleasedFlag NSSL_DEBUG_LINES =
+ new UnreleasedFlag(105);
- public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
- new BooleanFlag(106, false);
+ public static final UnreleasedFlag NSSL_DEBUG_REMOVE_ANIMATION =
+ new UnreleasedFlag(106);
- public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
- new BooleanFlag(107, false);
+ public static final UnreleasedFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
+ new UnreleasedFlag(107);
public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
- public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
- new BooleanFlag(109, false, true);
+ public static final UnreleasedFlag REMOVE_UNRANKED_NOTIFICATIONS =
+ new UnreleasedFlag(109, true);
- public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
- new BooleanFlag(110, false, true);
+ public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
+ new UnreleasedFlag(110, true);
/***************************************/
// 200 - keyguard/lockscreen
@@ -73,11 +73,11 @@
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
- public static final BooleanFlag LOCKSCREEN_ANIMATIONS =
- new BooleanFlag(201, true);
+ public static final ReleasedFlag LOCKSCREEN_ANIMATIONS =
+ new ReleasedFlag(201);
- public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
- new BooleanFlag(202, true);
+ public static final ReleasedFlag NEW_UNLOCK_SWIPE_ANIMATION =
+ new ReleasedFlag(202);
public static final ResourceBooleanFlag CHARGING_RIPPLE =
new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
@@ -92,25 +92,28 @@
* Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
* one.
*/
- public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(
- 206,
- /* default= */ false,
- /* teamfood= */ true);
+ public static final UnreleasedFlag MODERN_BOTTOM_AREA = new UnreleasedFlag(206, true);
- public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
+ public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
+
+ /**
+ * Flag to enable the usage of the new bouncer data source. This is a refactor of and
+ * eventual replacement of KeyguardBouncer.java.
+ */
+ public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
/***************************************/
// 300 - power menu
- public static final BooleanFlag POWER_MENU_LITE =
- new BooleanFlag(300, true);
+ public static final ReleasedFlag POWER_MENU_LITE =
+ new ReleasedFlag(300);
/***************************************/
// 400 - smartspace
- public static final BooleanFlag SMARTSPACE_DEDUPING =
- new BooleanFlag(400, true);
+ public static final ReleasedFlag SMARTSPACE_DEDUPING =
+ new ReleasedFlag(400);
- public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
- new BooleanFlag(401, true);
+ public static final ReleasedFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+ new ReleasedFlag(401);
public static final ResourceBooleanFlag SMARTSPACE =
new ResourceBooleanFlag(402, R.bool.flag_smartspace);
@@ -121,11 +124,11 @@
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_USER_SWITCHER =
- new BooleanFlag(500, true);
+ public static final ReleasedFlag NEW_USER_SWITCHER =
+ new ReleasedFlag(500);
- public static final BooleanFlag COMBINED_QS_HEADERS =
- new BooleanFlag(501, false, true);
+ public static final UnreleasedFlag COMBINED_QS_HEADERS =
+ new UnreleasedFlag(501, true);
public static final ResourceBooleanFlag PEOPLE_TILE =
new ResourceBooleanFlag(502, R.bool.flag_conversations);
@@ -137,35 +140,32 @@
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, true);
+ public static final ReleasedFlag NEW_FOOTER = new ReleasedFlag(504);
- public static final BooleanFlag NEW_HEADER = new BooleanFlag(505, false, true);
+ public static final UnreleasedFlag NEW_HEADER = new UnreleasedFlag(505, true);
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
/***************************************/
// 600- status bar
- public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
- new BooleanFlag(601, false);
-
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
- public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
- new BooleanFlag(603, false);
+ public static final UnreleasedFlag STATUS_BAR_LETTERBOX_APPEARANCE =
+ new UnreleasedFlag(603, false);
- public static final BooleanFlag NEW_STATUS_BAR_PIPELINE = new BooleanFlag(604, false);
+ public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE = new UnreleasedFlag(604, true);
/***************************************/
// 700 - dialer/calls
- public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
- new BooleanFlag(700, true);
+ public static final ReleasedFlag ONGOING_CALL_STATUS_BAR_CHIP =
+ new ReleasedFlag(700);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE =
- new BooleanFlag(701, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE =
+ new ReleasedFlag(701);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
- new BooleanFlag(702, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
+ new ReleasedFlag(702);
/***************************************/
// 800 - general visual/theme
@@ -174,21 +174,21 @@
/***************************************/
// 801 - region sampling
- public static final BooleanFlag REGION_SAMPLING =
- new BooleanFlag(801, false);
+ public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
/***************************************/
// 900 - media
- public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
- public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
- public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
- public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
+ public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
+ public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
+ public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
+ public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
// 1000 - dock
- public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
- new BooleanFlag(1000, true);
- public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true);
+ public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
+ new ReleasedFlag(1000);
+ public static final ReleasedFlag DOCK_SETUP_ENABLED = new ReleasedFlag(1001);
+ public static final UnreleasedFlag ROUNDED_BOX_RIPPLE = new UnreleasedFlag(1002, false);
// 1100 - windowing
@Keep
@@ -211,6 +211,14 @@
public static final SysPropBooleanFlag HIDE_NAVBAR_WINDOW =
new SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false);
+ @Keep
+ public static final SysPropBooleanFlag WM_DESKTOP_WINDOWING =
+ new SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false);
+
+ @Keep
+ public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
+ new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+
// 1200 - predictive back
@Keep
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
@@ -222,8 +230,12 @@
public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
- public static final BooleanFlag NEW_BACK_AFFORDANCE =
- new BooleanFlag(1203, false /* default */, true /* teamfood */);
+ public static final UnreleasedFlag NEW_BACK_AFFORDANCE =
+ new UnreleasedFlag(1203, false /* teamfood */);
+
+ // 1300 - screenshots
+
+ public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
new file mode 100644
index 0000000..fc5b9f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.flags
+
+import com.android.systemui.util.DeviceConfigProxy
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+interface ServerFlagReader {
+ /** Returns true if there is a server-side setting stored. */
+ fun hasOverride(flagId: Int): Boolean
+
+ /** Returns any stored server-side setting or the default if not set. */
+ fun readServerOverride(flagId: Int, default: Boolean): Boolean
+}
+
+class ServerFlagReaderImpl @Inject constructor(
+ private val deviceConfig: DeviceConfigProxy
+) : ServerFlagReader {
+ override fun hasOverride(flagId: Int): Boolean =
+ deviceConfig.getProperty(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId)
+ ) != null
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return deviceConfig.getBoolean(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId),
+ default
+ )
+ }
+
+ private fun getServerOverrideName(flagId: Int): String {
+ return "flag_override_$flagId"
+ }
+}
+
+private val SYSUI_NAMESPACE = "systemui"
+
+@Module
+interface ServerFlagReaderModule {
+ @Binds
+ fun bindsReader(impl: ServerFlagReaderImpl): ServerFlagReader
+}
+
+class ServerFlagReaderFake : ServerFlagReader {
+ private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+
+ override fun hasOverride(flagId: Int): Boolean {
+ return flagMap.containsKey(flagId)
+ }
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return flagMap.getOrDefault(flagId, default)
+ }
+
+ fun setFlagValue(flagId: Int, value: Boolean) {
+ flagMap.put(flagId, value)
+ }
+
+ fun eraseFlag(flagId: Int) {
+ flagMap.remove(flagId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 3202ecb..df44957 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -36,6 +36,7 @@
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
/** Home controls quick affordance data source. */
@@ -50,7 +51,13 @@
private val appContext = context.applicationContext
override val state: Flow<KeyguardQuickAffordanceConfig.State> =
- stateInternal(component.getControlsListingController().getOrNull())
+ component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
+ if (canShowWhileLocked) {
+ stateInternal(component.getControlsListingController().getOrNull())
+ } else {
+ flowOf(KeyguardQuickAffordanceConfig.State.Hidden)
+ }
+ }
override fun onQuickAffordanceClicked(
animationController: ActivityLaunchAnimator.Controller?,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index dc23684d..6124e10 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -22,17 +22,11 @@
import com.android.systemui.util.collection.RingBuffer
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
-import java.text.SimpleDateFormat
-import java.util.Arrays.stream
-import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread
import kotlin.math.max
-const val UNBOUNDED_STACK_TRACE = -1
-const val NESTED_TRACE_DEPTH = 10
-
/**
* A simple ring buffer of recyclable log messages
*
@@ -74,18 +68,12 @@
* @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
* out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
* the maximum, it behaves like a ring buffer.
- * @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
- * the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
- * @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
- * exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
*/
class LogBuffer @JvmOverloads constructor(
private val name: String,
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
- private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
- private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
) {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -236,7 +224,7 @@
val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
for (i in iterationStart until buffer.size) {
- dumpMessage(buffer[i], pw)
+ buffer[i].dump(pw)
}
}
@@ -264,76 +252,6 @@
}
}
- private fun dumpMessage(
- message: LogMessage,
- pw: PrintWriter
- ) {
- val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
- val shortLevel = message.level.shortString
- val messageToPrint = message.messagePrinter(message)
- val tag = message.tag
- printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
- message.exception?.let { ex ->
- printException(
- pw,
- formattedTimestamp,
- shortLevel,
- ex,
- tag,
- stackTraceDepth = rootStackTraceDepth)
- }
- }
-
- private fun printException(
- pw: PrintWriter,
- timestamp: String,
- level: String,
- exception: Throwable,
- tag: String,
- exceptionMessagePrefix: String = "",
- stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
- ) {
- val message = "$exceptionMessagePrefix$exception"
- printLikeLogcat(pw, timestamp, level, tag, message)
- var stacktraceStream = stream(exception.stackTrace)
- if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
- stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
- }
- stacktraceStream.forEach { line ->
- printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
- }
- exception.cause?.let { cause ->
- printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
- }
- exception.suppressedExceptions.forEach { suppressed ->
- printException(
- pw,
- timestamp,
- level,
- suppressed,
- tag,
- "Suppressed: ",
- nestedStackTraceDepth
- )
- }
- }
-
- private fun printLikeLogcat(
- pw: PrintWriter,
- formattedTimestamp: String,
- shortLogLevel: String,
- tag: String,
- message: String
- ) {
- pw.print(formattedTimestamp)
- pw.print(" ")
- pw.print(shortLogLevel)
- pw.print(" ")
- pw.print(tag)
- pw.print(": ")
- pw.println(message)
- }
-
private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
if (toLogcat || toSystrace) {
val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@
typealias MessageInitializer = LogMessage.() -> Unit
private const val TAG = "LogBuffer"
-private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
index 987aea8..dae2592 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -16,6 +16,10 @@
package com.android.systemui.log
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+
/**
* Generic data class for storing messages logged to a [LogBuffer]
*
@@ -50,6 +54,17 @@
var bool2: Boolean
var bool3: Boolean
var bool4: Boolean
+
+ /**
+ * Function that dumps the [LogMessage] to the provided [writer].
+ */
+ fun dump(writer: PrintWriter) {
+ val formattedTimestamp = DATE_FORMAT.format(timestamp)
+ val shortLevel = level.shortString
+ val messageToPrint = messagePrinter(this)
+ printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
+ exception?.printStackTrace(writer)
+ }
}
/**
@@ -61,3 +76,21 @@
* of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
*/
typealias MessagePrinter = LogMessage.() -> String
+
+private fun printLikeLogcat(
+ pw: PrintWriter,
+ formattedTimestamp: String,
+ shortLogLevel: String,
+ tag: String,
+ message: String
+) {
+ pw.print(formattedTimestamp)
+ pw.print(" ")
+ pw.print(shortLogLevel)
+ pw.print(" ")
+ pw.print(tag)
+ pw.print(": ")
+ pw.println(message)
+}
+
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 81efdf5..e0c8d66 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -266,11 +266,11 @@
}
/**
- * Are there any media notifications active, including the recommendation?
+ * Are there any active media entries, including the recommendation?
*/
- fun hasActiveMediaOrRecommendation() =
- userEntries.any { it.value.active } ||
- (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
+ fun hasActiveMediaOrRecommendation() = userEntries.any { it.value.active } ||
+ (smartspaceMediaData.isActive &&
+ (smartspaceMediaData.isValid() || reactivatedKey != null))
/**
* Are there any media entries we should display?
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 e9b6af4..e360d10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -263,6 +263,7 @@
}
private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+ disableSeekBar();
if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
mController.addDeviceToPlayMedia(device);
} else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index e6116c1..43b0287 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -273,6 +273,8 @@
void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
if (!mController.isVolumeControlEnabled(device)) {
disableSeekBar();
+ } else {
+ enableSeekBar();
}
mSeekBar.setMaxVolume(device.getMaxVolume());
final int currentVolume = device.getCurrentVolume();
@@ -417,11 +419,16 @@
return drawable;
}
- private void disableSeekBar() {
+ protected void disableSeekBar() {
mSeekBar.setEnabled(false);
mSeekBar.setOnTouchListener((v, event) -> true);
}
+ private void enableSeekBar() {
+ mSeekBar.setEnabled(true);
+ mSeekBar.setOnTouchListener((v, event) -> false);
+ }
+
protected void setUpDeviceIcon(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> {
Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 5d7af52..6fe06e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -194,6 +194,11 @@
}
private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
+ if (device == null) {
+ return isSourceDevice
+ ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
+ : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return isSourceDevice
@@ -229,6 +234,9 @@
}
private int getInteractionDeviceType(MediaDevice device) {
+ if (device == null) {
+ return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 5f478ce..9ab83b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -56,7 +56,7 @@
internal val logger: MediaTttLogger,
internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
+ @Main internal val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@@ -205,13 +205,15 @@
*
* @param appPackageName the package name of the app playing the media. Will be used to fetch
* the app icon and app name if overrides aren't provided.
+ *
+ * @return the content description of the icon.
*/
internal fun setIcon(
currentChipView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
- ) {
+ ): CharSequence {
val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
@@ -224,6 +226,7 @@
appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
+ return appIconView.contentDescription.toString()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 3ea11b8..b94b8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -122,13 +122,12 @@
val chipState = newChipInfo.state
// App icon
- setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+ val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
// Text
val otherDeviceName = newChipInfo.routeInfo.name.toString()
- currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = chipState.getChipTextString(context, otherDeviceName)
- }
+ val chipText = chipState.getChipTextString(context, otherDeviceName)
+ currentChipView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
currentChipView.requireViewById<View>(R.id.loading).visibility =
@@ -145,17 +144,29 @@
// Failure
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
+
+ // For accessibility
+ currentChipView.requireViewById<ViewGroup>(
+ R.id.media_ttt_sender_chip_inner
+ ).contentDescription = "$iconName $chipText"
}
override fun animateChipIn(chipView: ViewGroup) {
+ val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
- chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
+ chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
Interpolators.EMPHASIZED_DECELERATE,
- duration = 500L,
+ duration = ANIMATION_DURATION,
includeMargins = true,
includeFadeIn = true,
)
+
+ // We can only request focus once the animation finishes.
+ mainExecutor.executeDelayed(
+ { chipInnerView.requestAccessibilityFocus() },
+ ANIMATION_DURATION
+ )
}
override fun removeChip(removalReason: String) {
@@ -186,3 +197,4 @@
}
const val SENDER_TAG = "MediaTapToTransferSender"
+private const val ANIMATION_DURATION = 500L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 6bc50a6..da9fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,6 +48,7 @@
import androidx.annotation.NonNull;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -89,6 +90,7 @@
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ private final KeyguardViewController mKeyguardViewController;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -123,6 +125,7 @@
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ KeyguardViewController keyguardViewController,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -131,6 +134,7 @@
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mKeyguardViewController = keyguardViewController;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -317,8 +321,12 @@
* {@link InputMethodService} and the keyguard states.
*/
public boolean isImeShown(int vis) {
- View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
- boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
+ View shadeWindowView = null;
+ if (mCentralSurfacesOptionalLazy.get().isPresent()) {
+ shadeWindowView =
+ mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+ }
+ boolean isKeyguardShowing = mKeyguardViewController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 2d7a809..3789cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -302,10 +302,6 @@
*/
@VisibleForTesting
void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
- if (initializeTaskbarIfNecessary()) {
- return;
- }
-
if (display == null) {
return;
}
@@ -315,7 +311,7 @@
// We may show TaskBar on the default display for large screen device. Don't need to create
// navigation bar for this case.
- if (mIsTablet && isOnDefaultDisplay) {
+ if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b05e75e..6e927b0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -42,7 +42,6 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
-import com.android.wm.shell.back.BackAnimation
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.abs
@@ -119,7 +118,7 @@
private val latencyTracker: LatencyTracker
) {
/** Construct a [BackPanelController]. */
- fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+ fun create(context: Context): BackPanelController {
val backPanelController = BackPanelController(
context,
windowManager,
@@ -418,6 +417,7 @@
stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
GestureState.INACTIVE ->
mView.resetStretch()
+ else -> {}
}
// set y translation
@@ -510,7 +510,6 @@
private fun playCommitBackAnimation() {
// Check if we should vibrate again
if (previousState != GestureState.FLUNG) {
- backCallback.triggerBack()
velocityTracker!!.computeCurrentVelocity(1000)
val isSlow = abs(velocityTracker!!.xVelocity) < 500
val hasNotVibratedRecently =
@@ -519,6 +518,10 @@
vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
}
}
+ // Dispatch the actual back trigger
+ if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
+ backCallback.triggerBack()
+
playAnimation(setGoneEndListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 057ed24..6ac3ead 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -60,6 +60,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -82,6 +83,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.util.Assert;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -191,6 +193,7 @@
private final int mDisplayId;
private final Executor mMainExecutor;
+ private final Executor mBackgroundExecutor;
private final Rect mPipExcludedBounds = new Rect();
private final Rect mNavBarOverlayExcludedBounds = new Rect();
@@ -251,6 +254,7 @@
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
private Map<String, Integer> mVocab;
private boolean mUseMLModel;
+ private boolean mMLModelIsLoading;
// minimum width below which we do not run the model
private int mMLEnableWidth;
private float mMLModelThreshold;
@@ -318,6 +322,7 @@
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -334,6 +339,7 @@
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -568,10 +574,10 @@
private void resetEdgeBackPlugin() {
if (mIsNewBackAffordanceEnabled) {
setEdgeBackPlugin(
- mBackPanelControllerFactory.create(mContext, mBackAnimation));
+ mBackPanelControllerFactory.create(mContext));
} else {
setEdgeBackPlugin(
- new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ new NavigationBarEdgePanel(mContext, mLatencyTracker));
}
}
@@ -631,28 +637,63 @@
return;
}
- if (newState) {
- mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
- mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
- if (mBackGestureTfClassifierProvider.isActive()) {
- Trace.beginSection("EdgeBackGestureHandler#loadVocab");
- mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
- Trace.endSection();
- mUseMLModel = true;
+ mUseMLModel = newState;
+
+ if (mUseMLModel) {
+ Assert.isMainThread();
+ if (mMLModelIsLoading) {
+ Log.d(TAG, "Model tried to load while already loading.");
return;
}
- }
-
- mUseMLModel = false;
- if (mBackGestureTfClassifierProvider != null) {
+ mMLModelIsLoading = true;
+ mBackgroundExecutor.execute(() -> loadMLModel());
+ } else if (mBackGestureTfClassifierProvider != null) {
mBackGestureTfClassifierProvider.release();
mBackGestureTfClassifierProvider = null;
+ mVocab = null;
}
}
+ private void loadMLModel() {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
+ float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+ Map<String, Integer> vocab = null;
+ if (provider != null && !provider.isActive()) {
+ provider.release();
+ provider = null;
+ Log.w(TAG, "Cannot load model because it isn't active");
+ }
+ if (provider != null) {
+ Trace.beginSection("EdgeBackGestureHandler#loadVocab");
+ vocab = provider.loadVocab(mContext.getAssets());
+ Trace.endSection();
+ }
+ BackGestureTfClassifierProvider finalProvider = provider;
+ Map<String, Integer> finalVocab = vocab;
+ mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+ }
+
+ private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
+ Map<String, Integer> vocab, float threshold) {
+ Assert.isMainThread();
+ mMLModelIsLoading = false;
+ if (!mUseMLModel) {
+ // This can happen if the user disables Gesture Nav while the model is loading.
+ if (provider != null) {
+ provider.release();
+ }
+ Log.d(TAG, "Model finished loading but isn't needed.");
+ return;
+ }
+ mBackGestureTfClassifierProvider = provider;
+ mVocab = vocab;
+ mMLModelThreshold = threshold;
+ }
+
private int getBackGesturePredictionsCategory(int x, int y, int app) {
- if (app == -1) {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
+ if (provider == null || app == -1) {
return -1;
}
int distanceFromEdge;
@@ -673,7 +714,7 @@
new long[]{(long) y},
};
- mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+ mMLResults = provider.predict(featuresVector);
if (mMLResults == -1) {
return -1;
}
@@ -1031,6 +1072,7 @@
private final SysUiState mSysUiState;
private final PluginManager mPluginManager;
private final Executor mExecutor;
+ private final Executor mBackgroundExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
@@ -1050,6 +1092,7 @@
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -1067,6 +1110,7 @@
mSysUiState = sysUiState;
mPluginManager = pluginManager;
mExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
@@ -1089,6 +1133,7 @@
mSysUiState,
mPluginManager,
mExecutor,
+ mBackgroundExecutor,
mBroadcastDispatcher,
mProtoTracer,
mNavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index eba9d3f..122852f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -43,7 +43,6 @@
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import android.window.BackEvent;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -59,7 +58,6 @@
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.wm.shell.back.BackAnimation;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -283,14 +281,11 @@
}
};
private BackCallback mBackCallback;
- private BackAnimation mBackAnimation;
- public NavigationBarEdgePanel(Context context,
- BackAnimation backAnimation, LatencyTracker latencyTracker) {
+ public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
- mBackAnimation = backAnimation;
mVibratorHelper = Dependency.get(VibratorHelper.class);
mDensity = context.getResources().getDisplayMetrics().density;
@@ -360,7 +355,6 @@
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
mSwipeProgressThreshold = context.getResources()
.getDimension(R.dimen.navigation_edge_action_progress_threshold);
- initializeBackAnimation();
setVisibility(GONE);
@@ -388,17 +382,6 @@
mLatencyTracker = latencyTracker;
}
- public void setBackAnimation(BackAnimation backAnimation) {
- mBackAnimation = backAnimation;
- initializeBackAnimation();
- }
-
- private void initializeBackAnimation() {
- if (mBackAnimation != null) {
- mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold);
- }
- }
-
@Override
public void onDestroy() {
cancelFailsafe();
@@ -484,12 +467,6 @@
@Override
public void onMotionEvent(MotionEvent event) {
- if (mBackAnimation != null) {
- mBackAnimation.onBackMotion(
- event.getX(), event.getY(),
- event.getActionMasked(),
- mIsLeftPanel ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
- }
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -903,9 +880,6 @@
// Whenever the trigger back state changes the existing translation animation should be
// cancelled
mTranslationAnimation.cancel();
- if (mBackAnimation != null) {
- mBackAnimation.setTriggerBack(triggerBack);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 4552abd..77652c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -110,6 +110,11 @@
private Context mUserContext;
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
+ // Keep track of whether mTilesList contains the same information as the Settings value.
+ // This is a performance optimization to reduce the number of blocking calls to Settings from
+ // main thread.
+ // This is enforced by only cleaning the flag at the end of a successful run of #onTuningChanged
+ private boolean mTilesListDirty = true;
private final TileServiceRequestController mTileServiceRequestController;
private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
@@ -374,6 +379,7 @@
// the ones that are in the setting, update the Setting.
saveTilesToSettings(mTileSpecs);
}
+ mTilesListDirty = false;
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
}
@@ -436,6 +442,7 @@
);
}
+ // When calling this, you may want to modify mTilesListDirty accordingly.
@MainThread
private void saveTilesToSettings(List<String> tileSpecs) {
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
@@ -445,9 +452,15 @@
@MainThread
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
- final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
- final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+ final List<String> tileSpecs;
+ if (!mTilesListDirty) {
+ tileSpecs = new ArrayList<>(mTileSpecs);
+ } else {
+ tileSpecs = loadTileSpecs(mContext,
+ mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser));
+ }
if (changeFunction.test(tileSpecs)) {
+ mTilesListDirty = true;
saveTilesToSettings(tileSpecs);
}
}
@@ -507,6 +520,7 @@
}
}
if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
+ mTilesListDirty = true;
saveTilesToSettings(newTiles);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index ec0d081..eeb1010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,18 +134,9 @@
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
- List<String> rssiIgnoredSlots;
-
- if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_no_calling),
- getResources().getString(com.android.internal.R.string.status_bar_call_strength)
- );
- } else {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_mobile)
- );
- }
+ List<String> rssiIgnoredSlots = List.of(
+ getResources().getString(com.android.internal.R.string.status_bar_mobile)
+ );
mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index 2dac639..e925b54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,7 +27,6 @@
@JvmField val contentDescription: String? = null,
@JvmField val typeContentDescription: String? = null,
@JvmField val roaming: Boolean = false,
- @JvmField val providerModelBehavior: Boolean = false
) {
/**
* Changes the visibility of this state by returning a copy with the visibility changed.
@@ -41,4 +40,4 @@
if (this.visible == visible) return this
else return copy(visible = visible)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 592da65..703b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@
private View mSpacer;
@Nullable
private CellSignalState mLastSignalState;
- private boolean mProviderModelInitialized = false;
+ private boolean mMobileSignalInitialized = false;
private boolean mIsSingleCarrier;
public QSCarrier(Context context) {
@@ -96,35 +96,25 @@
mMobileRoaming.setImageTintList(colorStateList);
mMobileSignal.setImageTintList(colorStateList);
- if (state.providerModelBehavior) {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(
- mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
- }
- mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
- mMobileSignal.setContentDescription(state.contentDescription);
- } else {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
- }
- mMobileSignal.setImageLevel(state.mobileSignalIconId);
- StringBuilder contentDescription = new StringBuilder();
- if (state.contentDescription != null) {
- contentDescription.append(state.contentDescription).append(", ");
- }
- if (state.roaming) {
- contentDescription
- .append(mContext.getString(R.string.data_connection_roaming))
- .append(", ");
- }
- // TODO: show mobile data off/no internet text for 5 seconds before carrier text
- if (hasValidTypeContentDescription(state.typeContentDescription)) {
- contentDescription.append(state.typeContentDescription);
- }
- mMobileSignal.setContentDescription(contentDescription);
+ if (!mMobileSignalInitialized) {
+ mMobileSignalInitialized = true;
+ mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
}
+ mMobileSignal.setImageLevel(state.mobileSignalIconId);
+ StringBuilder contentDescription = new StringBuilder();
+ if (state.contentDescription != null) {
+ contentDescription.append(state.contentDescription).append(", ");
+ }
+ if (state.roaming) {
+ contentDescription
+ .append(mContext.getString(R.string.data_connection_roaming))
+ .append(", ");
+ }
+ // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+ if (hasValidTypeContentDescription(state.typeContentDescription)) {
+ contentDescription.append(state.typeContentDescription);
+ }
+ mMobileSignal.setContentDescription(contentDescription);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 209d09d..6a8bf75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,10 +42,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -78,7 +75,6 @@
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
private int[] mLastSignalLevel = new int[SIM_SLOTS];
private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
- private final boolean mProviderModel;
private final CarrierConfigTracker mCarrierConfigTracker;
private boolean mIsSingleCarrier;
@@ -90,9 +86,6 @@
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
- if (mProviderModel) {
- return;
- }
int slotIndex = getSlotIndex(indicators.subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -107,91 +100,12 @@
indicators.statusIcon.icon,
indicators.statusIcon.contentDescription,
indicators.typeContentDescription.toString(),
- indicators.roaming,
- mProviderModel
+ indicators.roaming
);
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@Override
- public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
- if (!mProviderModel) {
- return;
- }
- int slotIndex = getSlotIndex(subId);
- if (slotIndex >= SIM_SLOTS) {
- Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
- return;
- }
- if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
- return;
- }
-
- boolean displayCallStrengthIcon =
- mCarrierConfigTracker.getCallStrengthConfig(subId);
-
- if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
- if (statusIcon.visible) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- // Whenever the no Calling & SMS state is cleared, switched to the last
- // known call strength icon.
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- mLastSignalLevel[slotIndex],
- mLastSignalLevelDescription[slotIndex],
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- } else {
- mLastSignalLevel[slotIndex] = statusIcon.icon;
- mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
- // Only Shows the call strength icon when the no Calling & SMS icon is not
- // shown.
- if (mInfos[slotIndex].mobileSignalIconId
- != R.drawable.ic_qs_no_calling_sms) {
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- }
- }
- }
-
- @Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
@@ -219,14 +133,8 @@
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
- if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- mProviderModel = true;
- } else {
- mProviderModel = false;
- }
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
mNetworkController = networkController;
@@ -262,8 +170,7 @@
R.drawable.ic_qs_no_calling_sms,
context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
"",
- false,
- mProviderModel);
+ false);
mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
mLastSignalLevelDescription[i] =
context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -351,8 +258,7 @@
for (int i = 0; i < SIM_SLOTS; i++) {
if (mInfos[i].visible
&& mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
- mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
- mProviderModel);
+ mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
}
}
}
@@ -470,15 +376,13 @@
private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
private final Context mContext;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final SlotIndexResolver mSlotIndexResolver;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
@@ -486,7 +390,6 @@
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
mContext = context;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mSlotIndexResolver = slotIndexResolver;
}
@@ -498,7 +401,7 @@
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
mNetworkController, mCarrierTextControllerBuilder, mContext,
- mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+ mCarrierConfigTracker, mSlotIndexResolver);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 86ef858..ab795fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -69,36 +69,63 @@
})
}
- fun logTileClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logTileClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
+ int1 = eventId
str2 = StatusBarState.toString(statusBarState)
str3 = toStateString(state)
}, {
- "[$str1] Tile clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3"
})
}
- fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logHandleClick(tileSpec: String, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
- str2 = StatusBarState.toString(statusBarState)
- str3 = toStateString(state)
+ int1 = eventId
}, {
- "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile handling click."
})
}
- fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
+ int1 = eventId
str2 = StatusBarState.toString(statusBarState)
str3 = toStateString(state)
}, {
- "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3"
+ })
+ }
+
+ fun logHandleSecondaryClick(tileSpec: String, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
+ }, {
+ "[$str1][$int1] Tile handling secondary click."
+ })
+ }
+
+ fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
+ str2 = StatusBarState.toString(statusBarState)
+ str3 = toStateString(state)
+ }, {
+ "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ })
+ }
+
+ fun logHandleLongClick(tileSpec: String, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
+ }, {
+ "[$str1][$int1] Tile handling long click."
})
}
@@ -144,4 +171,4 @@
) {
buffer.log(TAG, logLevel, initializer, printer)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 740e12a..2cffe89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -105,6 +105,9 @@
private final FalsingManager mFalsingManager;
protected final QSLogger mQSLogger;
private volatile int mReadyState;
+ // Keeps track of the click event, to match it with the handling in the background thread
+ // Only read and modified in main thread (where click events come through).
+ private int mClickEventId = 0;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
@@ -295,9 +298,11 @@
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
getInstanceId());
- mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
+ final int eventId = mClickEventId++;
+ mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+ eventId);
if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mHandler.obtainMessage(H.CLICK, view).sendToTarget();
+ mHandler.obtainMessage(H.CLICK, eventId, 0, view).sendToTarget();
}
}
@@ -307,9 +312,10 @@
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_SECONDARY_CLICK, 0, getMetricsSpec(),
getInstanceId());
+ final int eventId = mClickEventId++;
mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
- mState.state);
- mHandler.obtainMessage(H.SECONDARY_CLICK, view).sendToTarget();
+ mState.state, eventId);
+ mHandler.obtainMessage(H.SECONDARY_CLICK, eventId, 0, view).sendToTarget();
}
@Override
@@ -319,8 +325,10 @@
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
getInstanceId());
- mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
- mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
+ final int eventId = mClickEventId++;
+ mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+ eventId);
+ mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
}
public LogMaker populate(LogMaker logMaker) {
@@ -590,13 +598,16 @@
mContext, mEnforcedAdmin);
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} else {
+ mQSLogger.logHandleClick(mTileSpec, msg.arg1);
handleClick((View) msg.obj);
}
} else if (msg.what == SECONDARY_CLICK) {
name = "handleSecondaryClick";
+ mQSLogger.logHandleSecondaryClick(mTileSpec, msg.arg1);
handleSecondaryClick((View) msg.obj);
} else if (msg.what == LONG_CLICK) {
name = "handleLongClick";
+ mQSLogger.logHandleLongClick(mTileSpec, msg.arg1);
handleLongClick((View) msg.obj);
} else if (msg.what == REFRESH_STATE) {
name = "handleRefreshState";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f629a036..d5efe36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -806,7 +806,8 @@
return;
}
- mTelephonyManager.setDataEnabled(enabled);
+ mTelephonyManager.setDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
if (disableOtherSubscriptions) {
final List<SubscriptionInfo> subInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index 0a8e6e2..56a1874 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -39,7 +39,7 @@
ROUNDED_BOX,
ELLIPSE
}
-
+ //language=AGSL
companion object {
private const val SHADER_UNIFORMS = """uniform vec2 in_center;
uniform vec2 in_size;
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
index 0cacbc2..6de4648 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
@@ -17,6 +17,7 @@
/** A common utility functions that are used for computing [RippleShader]. */
class RippleShaderUtilLibrary {
+ //language=AGSL
companion object {
const val SHADER_LIB = """
float triangleNoise(vec2 n) {
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index 83d9f2d..8b01201 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -39,7 +39,9 @@
open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private lateinit var rippleShader: RippleShader
- private lateinit var rippleShape: RippleShape
+ lateinit var rippleShape: RippleShape
+ private set
+
private val ripplePaint = Paint()
var rippleInProgress: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
index 7f26146..5e256c6 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
@@ -17,6 +17,7 @@
/** Library class that contains 2D signed distance functions. */
class SdfShaderLibrary {
+ //language=AGSL
companion object {
const val CIRCLE_SDF = """
float sdCircle(vec2 p, float r) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
new file mode 100644
index 0000000..39f35a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+interface ImageCapture {
+
+ fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
+
+ fun captureTask(taskId: Int): Bitmap?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
new file mode 100644
index 0000000..258c436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.IBinder
+import android.util.Log
+import android.view.DisplayAddress
+import android.view.SurfaceControl
+import android.view.SurfaceControl.DisplayCaptureArgs
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import androidx.annotation.VisibleForTesting
+import javax.inject.Inject
+
+private const val TAG = "ImageCaptureImpl"
+
+open class ImageCaptureImpl @Inject constructor(
+ private val displayManager: DisplayManager,
+ private val atmService: IActivityTaskManager
+) : ImageCapture {
+
+ override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+ val width = crop?.width() ?: 0
+ val height = crop?.height() ?: 0
+ val sourceCrop = crop ?: Rect()
+ val displayToken = physicalDisplayToken(displayId) ?: return null
+ val buffer = captureDisplay(displayToken, width, height, sourceCrop)
+
+ return buffer?.asBitmap()
+ }
+
+ override fun captureTask(taskId: Int): Bitmap? {
+ val snapshot = atmService.takeTaskSnapshot(taskId)
+ return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+ }
+
+ @VisibleForTesting
+ open fun physicalDisplayToken(displayId: Int): IBinder? {
+ val display = displayManager.getDisplay(displayId)
+ if (display == null) {
+ Log.e(TAG, "No display with id: $displayId")
+ return null
+ }
+ val address = display.address
+ if (address !is DisplayAddress.Physical) {
+ Log.e(TAG, "Display does not have a physical address: $display")
+ return null
+ }
+ return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId)
+ }
+
+ @VisibleForTesting
+ open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
+ val captureArgs = DisplayCaptureArgs.Builder(displayToken)
+ .setSize(width, height)
+ .setSourceCrop(crop)
+ .build()
+ return SurfaceControl.captureDisplay(captureArgs)
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
new file mode 100644
index 0000000..beb54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.net.Uri
+import android.util.Log
+import android.view.WindowManager.ScreenshotType
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ */
+@SysUISingleton
+internal class RequestProcessor @Inject constructor(
+ private val controller: ScreenshotController,
+) {
+ fun processRequest(
+ @ScreenshotType type: Int,
+ onSavedListener: Consumer<Uri>,
+ request: ScreenshotRequest,
+ callback: RequestCallback
+ ) {
+
+ if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+
+ controller.handleImageAsScreenshot(
+ image, request.boundsInScreen, request.insets,
+ request.taskId, request.userId, request.topComponent, onSavedListener, callback
+ )
+ return
+ }
+
+ when (type) {
+ TAKE_SCREENSHOT_FULLSCREEN ->
+ controller.takeScreenshotFullscreen(null, onSavedListener, callback)
+ TAKE_SCREENSHOT_SELECTED_REGION ->
+ controller.takeScreenshotPartial(null, onSavedListener, callback)
+ else -> Log.w(TAG, "Invalid screenshot option: $type")
+ }
+ }
+
+ companion object {
+ const val TAG: String = "RequestProcessor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 82de389..69ee8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -57,14 +57,12 @@
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
@@ -72,7 +70,6 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -249,6 +246,7 @@
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
private final ImageExporter mImageExporter;
+ private final ImageCapture mImageCapture;
private final Executor mMainExecutor;
private final ExecutorService mBgExecutor;
private final BroadcastSender mBroadcastSender;
@@ -295,6 +293,7 @@
ScrollCaptureClient scrollCaptureClient,
UiEventLogger uiEventLogger,
ImageExporter imageExporter,
+ ImageCapture imageCapture,
@Main Executor mainExecutor,
ScrollCaptureController scrollCaptureController,
LongScreenshotData longScreenshotHolder,
@@ -308,6 +307,7 @@
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
+ mImageCapture = imageCapture;
mMainExecutor = mainExecutor;
mScrollCaptureController = scrollCaptureController;
mLongScreenshotHolder = longScreenshotHolder;
@@ -531,7 +531,7 @@
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
- Bitmap screenshot = captureScreenshot(crop);
+ Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -549,32 +549,6 @@
ClipboardOverlayController.SELF_PERMISSION);
}
- private Bitmap captureScreenshot(Rect crop) {
- int width = crop.width();
- int height = crop.height();
- Bitmap screenshot = null;
- final Display display = getDefaultDisplay();
- final DisplayAddress address = display.getAddress();
- if (!(address instanceof DisplayAddress.Physical)) {
- Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
- + display);
- } else {
- final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-
- final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
- physicalAddress.getPhysicalDisplayId());
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
- .setSourceCrop(crop)
- .setSize(width, height)
- .build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
- screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
- }
- return screenshot;
- }
-
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash) {
withWindowAttached(() ->
@@ -720,7 +694,7 @@
mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
- Bitmap newScreenshot = captureScreenshot(
+ Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 32d8203..f1f0223 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -57,6 +58,8 @@
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FlagListenable.FlagEvent;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -75,6 +78,8 @@
private final Handler mHandler;
private final Context mContext;
private final @Background Executor mBgExecutor;
+ private final RequestProcessor mProcessor;
+ private final FeatureFlags mFeatureFlags;
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
@Override
@@ -104,7 +109,8 @@
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
ScreenshotNotificationsController notificationsController, Context context,
- @Background Executor bgExecutor) {
+ @Background Executor bgExecutor, FeatureFlags featureFlags,
+ RequestProcessor processor) {
if (DEBUG_SERVICE) {
Log.d(TAG, "new " + this);
}
@@ -116,6 +122,9 @@
mNotificationsController = notificationsController;
mContext = context;
mBgExecutor = bgExecutor;
+ mFeatureFlags = featureFlags;
+ mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+ mProcessor = processor;
}
@Override
@@ -218,6 +227,12 @@
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
topComponent == null ? "" : topComponent.getPackageName());
+ if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
+ Log.d(TAG, "handleMessage: Using request processor");
+ mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+ return true;
+ }
+
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 91ef3c3..3e442587 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -18,6 +18,8 @@
import android.app.Service;
+import com.android.systemui.screenshot.ImageCapture;
+import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
import dagger.Binds;
@@ -37,4 +39,6 @@
@ClassKey(TakeScreenshotService.class)
public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+ @Binds
+ public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 5793105..0f9ac36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -264,14 +264,8 @@
Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
)
- carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- listOf(
- header.context.getString(com.android.internal.R.string.status_bar_no_calling),
- header.context.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- } else {
+ carrierIconSlots =
listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
- }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(qsCarrierGroup)
.build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e36b67e..7f0e76b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1499,10 +1499,7 @@
}
private void updateKeyguardStatusViewAlignment(boolean animate) {
- boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
+ boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -1526,6 +1523,15 @@
mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
}
+ private boolean shouldKeyguardStatusViewBeCentered() {
+ boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
+ boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
+ return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
+ || hasPulsingNotifications();
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 1a8a6d1..4aad245 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -171,6 +171,7 @@
private float mInitialTouchY;
private float mInitialTouchX;
private boolean mTouchDisabled;
+ private boolean mInitialTouchFromKeyguard;
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
@@ -395,6 +396,7 @@
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
+ mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
if (startTracking) {
mTouchSlopExceeded = true;
setExpandedHeight(mInitialOffsetOnTouch);
@@ -417,20 +419,14 @@
mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
final boolean expand;
- if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
- // If the keyguard is fading away, don't expand it again. This can happen if you're
- // swiping to unlock, the app below the keyguard is in landscape, and the screen
- // rotates while your finger is still down after the swipe to unlock.
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- expand = false;
- } else if (onKeyguard) {
+ if (mKeyguardStateController.isKeyguardFadingAway()
+ || (mInitialTouchFromKeyguard && !onKeyguard)) {
+ // Don't expand for any touches that started from the keyguard and ended after the
+ // keyguard is gone.
+ expand = false;
+ } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ if (onKeyguard) {
expand = true;
- } else if (mKeyguardStateController.isKeyguardFadingAway()) {
- // If we're in the middle of dismissing the keyguard, don't expand due to the
- // cancelled gesture. Gesture cancellation during an unlock is expected in some
- // situations, such keeping your finger down while swiping to unlock to an app
- // that is locked in landscape (the rotation will cancel the touch event).
- expand = false;
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index 1b74ac3..b02a45a 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,7 +18,7 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
@@ -61,7 +61,7 @@
@Binds
@Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
abstract fun provideLockscreenSmartspaceTargetFilter(
- filter: LockscreenTargetFilter?
+ filter: LockscreenAndDreamTargetFilter?
): SmartspaceTargetFilter?
@Binds
@@ -69,4 +69,4 @@
abstract fun bindSmartspacePrecondition(
lockscreenPrecondition: LockscreenPrecondition?
): SmartspacePrecondition?
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
rename to packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
index 6ad4901..aa4cf75 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
@@ -32,9 +32,9 @@
import javax.inject.Inject
/**
- * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen.
+ * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams.
*/
-class LockscreenTargetFilter @Inject constructor(
+class LockscreenAndDreamTargetFilter @Inject constructor(
private val secureSettings: SecureSettings,
private val userTracker: UserTracker,
private val execution: Execution,
@@ -149,4 +149,4 @@
listeners.forEach { it.onCriteriaChanged() }
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 3013ad0..a57d849b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -61,21 +61,19 @@
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
private boolean mForceHidden;
- private boolean mProviderModel;
/**
* Designated constructor
*/
public static StatusBarMobileView fromContext(
Context context,
- String slot,
- boolean providerModel
+ String slot
) {
LayoutInflater inflater = LayoutInflater.from(context);
StatusBarMobileView v = (StatusBarMobileView)
inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
v.setSlot(slot);
- v.init(providerModel);
+ v.init();
v.setVisibleState(STATE_ICON);
return v;
}
@@ -108,17 +106,12 @@
outRect.bottom += translationY;
}
- private void init(boolean providerModel) {
- mProviderModel = providerModel;
+ private void init() {
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_group);
mMobile = findViewById(R.id.mobile_signal);
mMobileType = findViewById(R.id.mobile_type);
- if (mProviderModel) {
- mMobileRoaming = findViewById(R.id.mobile_roaming_large);
- } else {
- mMobileRoaming = findViewById(R.id.mobile_roaming);
- }
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
mIn = findViewById(R.id.mobile_in);
mOut = findViewById(R.id.mobile_out);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 6914ae6..1638780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -21,6 +21,7 @@
import android.telephony.SubscriptionInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
@@ -36,6 +37,7 @@
* Implements network listeners and forwards the calls along onto other listeners but on
* the current or specified Looper.
*/
+@SysUISingleton
public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
private static final String TAG = "CallbackHandler";
private static final int MSG_EMERGENCE_CHANGED = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index f2014e9..ec221b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,25 +26,17 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
-import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsRegistrationAttributes;
-import android.telephony.ims.RegistrationManager.RegistrationCallback;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
@@ -54,8 +46,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -70,33 +60,22 @@
public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final int STATUS_HISTORY_SIZE = 64;
- private static final int IMS_TYPE_WWAN = 1;
- private static final int IMS_TYPE_WLAN = 2;
- private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
private final TelephonyManager mPhone;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final ImsMmTelManager mImsMmTelManager;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
- private final boolean mProviderModelBehavior;
- private final Handler mReceiverHandler;
- private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
private Map<String, MobileIconGroup> mNetworkToIconLookup;
- private int mLastLevel;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- private int mLastWwanLevel;
- private int mLastWlanLevel;
- private int mLastWlanCrossSimLevel;
@VisibleForTesting
- MobileStatusTracker mMobileStatusTracker;
+ final MobileStatusTracker mMobileStatusTracker;
// Save the previous STATUS_HISTORY_SIZE states for logging.
private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -133,52 +112,6 @@
}
};
- private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
- @Override
- public void onRegistered(ImsRegistrationAttributes attributes) {
- Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
- int imsTransportType = attributes.getTransportType();
- int registrationAttributes = attributes.getAttributeFlags();
- if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- if (registrationAttributes == 0) {
- mImsType = IMS_TYPE_WLAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (registrationAttributes
- == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
- mImsType = IMS_TYPE_WLAN_CROSS_SIM;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(
- mLastWlanCrossSimLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- Log.d(mTag, "onDeregistered: " + "info=" + info);
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- };
-
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(
@@ -192,7 +125,7 @@
SubscriptionDefaults defaults,
Looper receiverLooper,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags
+ MobileStatusTrackerFactory mobileStatusTrackerFactory
) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -206,7 +139,6 @@
R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
- mReceiverHandler = new Handler(receiverLooper);
mNetworkToIconLookup = mapIconSets(mConfig);
mDefaultIcons = getDefaultIcons(mConfig);
@@ -223,10 +155,7 @@
updateTelephony();
}
};
- mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
- mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
- info, mDefaults, mMobileCallback);
- mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
+ mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
}
void setConfiguration(Config config) {
@@ -271,41 +200,14 @@
mContext.getContentResolver().registerContentObserver(Global.getUriFor(
Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
true, mObserver);
- if (mProviderModelBehavior) {
- mReceiverHandler.post(mTryRegisterIms);
- }
}
- // There is no listener to monitor whether the IMS service is ready, so we have to retry the
- // IMS registration.
- private final Runnable mTryRegisterIms = new Runnable() {
- private static final int MAX_RETRY = 12;
- private int mRetryCount;
-
- @Override
- public void run() {
- try {
- mRetryCount++;
- mImsMmTelManager.registerImsRegistrationCallback(
- mReceiverHandler::post, mRegistrationCallback);
- Log.d(mTag, "registerImsRegistrationCallback succeeded");
- } catch (RuntimeException | ImsException e) {
- if (mRetryCount < MAX_RETRY) {
- Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
- // Wait for 5 seconds to retry
- mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
- }
- }
- }
- };
-
/**
* Stop listening for phone state changes.
*/
public void unregisterListener() {
mMobileStatusTracker.setListening(false);
mContext.getContentResolver().unregisterContentObserver(mObserver);
- mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
}
private void updateInflateSignalStrength() {
@@ -394,7 +296,7 @@
CharSequence qsDescription = null;
if (mCurrentState.dataSim) {
- // If using provider model behavior, only show QS icons if the state is also default
+ // only show QS icons if the state is also default
if (!mCurrentState.isDefault) {
return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
@@ -416,32 +318,15 @@
private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
- boolean showTriangle = false;
- int typeIcon = 0;
- IconState statusIcon = null;
+ IconState statusIcon = new IconState(
+ mCurrentState.enabled && !mCurrentState.airplaneMode,
+ getCurrentIconId(), contentDescription);
- if (mProviderModelBehavior) {
- boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
- && (mCurrentState.dataSim && mCurrentState.isDefault);
- typeIcon =
- (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showDataIconStatusBar |= mCurrentState.roaming;
- statusIcon = new IconState(
- showDataIconStatusBar && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
-
- showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
- } else {
- statusIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
-
- boolean showDataIconInStatusBar =
- (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
- typeIcon =
- (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
- }
+ boolean showDataIconInStatusBar =
+ (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+ int typeIcon =
+ (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
return new SbInfo(showTriangle, typeIcon, statusIcon);
}
@@ -560,144 +445,7 @@
}
private void updateMobileStatus(MobileStatus mobileStatus) {
- int lastVoiceState = mCurrentState.getVoiceServiceState();
mCurrentState.setFromMobileStatus(mobileStatus);
-
- notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
- if (mProviderModelBehavior) {
- maybeNotifyCallStateChanged(lastVoiceState);
- }
- }
-
- /** Call state changed is only applicable when provider model behavior is true */
- private void maybeNotifyCallStateChanged(int lastVoiceState) {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- if (lastVoiceState == currentVoiceState) {
- return;
- }
- // Only update the no calling Status in the below scenarios
- // 1. The first valid voice state has been received
- // 2. The voice state has been changed and either the last or current state is
- // ServiceState.STATE_IN_SERVICE
- if (lastVoiceState == -1
- || (lastVoiceState == ServiceState.STATE_IN_SERVICE
- || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
-
- void updateNoCallingState() {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- private boolean hideNoCalling() {
- return mNetworkController.hasDefaultNetwork()
- && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
- }
-
- private int getCallStrengthIcon(int level, boolean isWifi) {
- return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
- : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
- }
-
- private String getCallStrengthDescription(int level, boolean isWifi) {
- return isWifi
- ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
- .toString()
- : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
- .toString();
- }
-
- void refreshCallIndicator(SignalCallback callback) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-
- switch (mImsType) {
- case IMS_TYPE_WWAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- break;
- case IMS_TYPE_WLAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- break;
- case IMS_TYPE_WLAN_CROSS_SIM:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
- }
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyWifiLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanLevel = level;
- if (mImsType != IMS_TYPE_WLAN) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */true),
- getCallStrengthDescription(level, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanCrossSimLevel = level;
- if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */false),
- getCallStrengthDescription(level, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
- if (!mProviderModelBehavior) {
- return;
- }
- int newLevel = getSignalLevel(signalStrength);
- if (newLevel != mLastLevel) {
- mLastLevel = newLevel;
- mLastWwanLevel = newLevel;
- if (mImsType == IMS_TYPE_WWAN) {
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(newLevel, /* isWifi= */false),
- getCallStrengthDescription(newLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- if (mCurrentState.dataSim) {
- mNetworkController.notifyDefaultMobileLevelChange(newLevel);
- }
- }
}
int getSignalLevel(SignalStrength signalStrength) {
@@ -801,19 +549,14 @@
mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
}
- @VisibleForTesting
- void setImsType(int imsType) {
- mImsType = imsType;
- }
-
@Override
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
- pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
+ pw.println(" mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
pw.println(" MobileStatusHistory");
int size = 0;
for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -843,6 +586,11 @@
icon = iconState;
description = desc;
}
+
+ @Override
+ public String toString() {
+ return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
+ }
}
/** Box for status bar icon info */
@@ -856,5 +604,11 @@
ratTypeIcon = typeIcon;
icon = iconState;
}
+
+ @Override
+ public String toString() {
+ return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
+ + " icon=" + icon;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
new file mode 100644
index 0000000..7938179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.content.Context
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.MobileStatusTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.CarrierConfigTracker
+import javax.inject.Inject
+
+/**
+ * Factory to make MobileSignalController injectable
+ */
+@SysUISingleton
+internal class MobileSignalControllerFactory @Inject constructor(
+ val context: Context,
+ val callbackHandler: CallbackHandler,
+ val carrierConfigTracker: CarrierConfigTracker,
+) {
+ fun createMobileSignalController(
+ config: MobileMappings.Config,
+ hasMobileData: Boolean,
+ phone: TelephonyManager,
+ networkController: NetworkControllerImpl,
+ subscriptionInfo: SubscriptionInfo,
+ subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
+ receiverLooper: Looper,
+ ): MobileSignalController {
+ val mobileTrackerFactory = MobileStatusTrackerFactory(
+ phone,
+ receiverLooper,
+ subscriptionInfo,
+ subscriptionDefaults)
+
+ return MobileSignalController(
+ context,
+ config,
+ hasMobileData,
+ phone,
+ callbackHandler,
+ networkController,
+ subscriptionInfo,
+ subscriptionDefaults,
+ receiverLooper,
+ carrierConfigTracker,
+ mobileTrackerFactory,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
new file mode 100644
index 0000000..a4c1a198
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileStatusTracker
+
+/**
+ * Factory for [MobileStatusTracker], which lives in SettingsLib
+ */
+class MobileStatusTrackerFactory (
+ val phone: TelephonyManager,
+ val receiverLooper: Looper,
+ val info: SubscriptionInfo,
+ val defaults: MobileStatusTracker.SubscriptionDefaults,
+) {
+ fun createTracker(
+ callback: MobileStatusTracker.Callback
+ ): MobileStatusTracker {
+ return MobileStatusTracker(
+ phone,
+ receiverLooper,
+ info,
+ defaults,
+ callback)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 268531d..ea7ec4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,8 +71,6 @@
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -133,12 +131,11 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final DemoModeController mDemoModeController;
private final Object mLock = new Object();
- private final boolean mProviderModelBehavior;
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
private final LogBuffer mLogBuffer;
+ private final MobileSignalControllerFactory mMobileFactory;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -234,9 +231,9 @@
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
- FeatureFlags featureFlags,
DumpManager dumpManager,
@StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
@@ -256,8 +253,8 @@
demoModeController,
carrierConfigTracker,
trackerFactory,
+ mobileFactory,
handler,
- featureFlags,
dumpManager,
logBuffer);
mReceiverHandler.post(mRegisterListeners);
@@ -282,8 +279,8 @@
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
- FeatureFlags featureFlags,
DumpManager dumpManager,
LogBuffer logBuffer
) {
@@ -297,6 +294,7 @@
mCallbackHandler = callbackHandler;
mDataSaverController = new DataSaverControllerImpl(context);
mBroadcastDispatcher = broadcastDispatcher;
+ mMobileFactory = mobileFactory;
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
@@ -304,7 +302,6 @@
mHasMobileDataFeature = telephonyManager.isDataCapable();
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mLogBuffer = logBuffer;
@@ -456,7 +453,6 @@
};
mDemoModeController.addCallback(this);
- mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
mDumpManager.registerDumpable(TAG, this);
}
@@ -497,16 +493,16 @@
// broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
- filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
- filter.addAction(Intent.ACTION_SERVICE_STATE);
- filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(Intent.ACTION_SERVICE_STATE);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -659,20 +655,6 @@
return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
}
- void notifyWifiLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyWifiLevelChange(level);
- }
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyDefaultMobileLevelChange(level);
- }
- }
-
private void notifyControllersMobileDataChanged() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -745,9 +727,6 @@
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners(cb);
- if (mProviderModelBehavior) {
- mobileSignalController.refreshCallIndicator(cb);
- }
}
mCallbackHandler.setListening(cb, true);
}
@@ -862,9 +841,6 @@
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.setConfiguration(mConfig);
- if (mProviderModelBehavior) {
- controller.refreshCallIndicator(mCallbackHandler);
- }
}
refreshLocale();
}
@@ -981,11 +957,15 @@
mMobileSignalControllers.put(subId, cachedControllers.get(subId));
cachedControllers.remove(subId);
} else {
- MobileSignalController controller = new MobileSignalController(mContext, mConfig,
- mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
- mCallbackHandler, this, subscriptions.get(i),
- mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(subId),
+ this,
+ subscriptions.get(i),
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1139,24 +1119,11 @@
|| mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
pushConnectivityToSignals();
- if (mProviderModelBehavior) {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.updateNoCallingState();
- }
- notifyAllListeners();
- } else {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- }
+ mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+ mNoNetworksAvailable);
}
/**
@@ -1346,7 +1313,7 @@
mMobileSignalControllers.clear();
int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
for (int i = start /* get out of normal index range */; i < start + num; i++) {
- subs.add(addSignalController(i, i));
+ subs.add(addDemoModeSignalController(i, i));
}
mCallbackHandler.setSubs(subs);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1372,7 +1339,7 @@
List<SubscriptionInfo> subs = new ArrayList<>();
while (mMobileSignalControllers.size() <= slot) {
int nextSlot = mMobileSignalControllers.size();
- subs.add(addSignalController(nextSlot, nextSlot));
+ subs.add(addDemoModeSignalController(nextSlot, nextSlot));
}
if (!subs.isEmpty()) {
mCallbackHandler.setSubs(subs);
@@ -1462,14 +1429,20 @@
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
- private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
+ private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
- MobileSignalController controller = new MobileSignalController(mContext,
- mConfig, mHasMobileDataFeature,
- mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
- info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(info.getSubscriptionId()),
+ this,
+ info,
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
+
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 87cdb17..12f2c22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,7 +222,6 @@
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
- boolean levelChanged = mCurrentState.level != mWifiTracker.level;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -230,10 +229,6 @@
mCurrentState.iconGroup =
mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
: mUnmergedWifiIconGroup;
-
- if (levelChanged) {
- mNetworkController.notifyWifiLevelChange(mCurrentState.level);
- }
}
boolean isCarrierMergedWifi(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 48e3450..0951e82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,7 +19,9 @@
import android.app.IActivityManager;
import android.content.Context;
import android.os.Handler;
+import android.os.RemoteException;
import android.service.dreams.IDreamManager;
+import android.util.Log;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
@@ -60,10 +62,12 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tracing.ProtoTracer;
@@ -274,7 +278,30 @@
@Provides
@SysUISingleton
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+ KeyguardStateController keyguardStateController,
+ Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager,
InteractionJankMonitor interactionJankMonitor) {
- return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
+ DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
+ @Override
+ public boolean isDreaming() {
+ try {
+ return dreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isUnlocked() {
+ return keyguardStateController.isUnlocked();
+ }
+
+ @Override
+ public boolean isShowingAlternateAuthOnUnlock() {
+ return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+ }
+ };
+ return new DialogLaunchAnimator(callback, interactionJankMonitor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 126a986..dbf4810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,8 +18,10 @@
import android.animation.ObjectAnimator
import android.util.FloatProperty
+import com.android.systemui.Dumpable
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,17 +34,20 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
+ dumpManager: DumpManager,
private val mHeadsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
+ Dumpable {
private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
"notificationVisibility") {
@@ -60,6 +65,7 @@
private var mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
+ private var mDozeAmountSource: String = "init"
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -142,6 +148,7 @@
}
init {
+ dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
@@ -248,13 +255,14 @@
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
- setDozeAmount(linear, eased)
+ setDozeAmount(linear, eased, source = "StatusBar")
}
- fun setDozeAmount(linear: Float, eased: Float) {
+ fun setDozeAmount(linear: Float, eased: Float, source: String) {
val changed = linear != mLinearDozeAmount
mLinearDozeAmount = linear
mDozeAmount = eased
+ mDozeAmountSource = source
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
@@ -271,7 +279,7 @@
// undefined state, so it's an indication that we should do state cleanup. We override
// the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
// See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
- setDozeAmount(0f, 0f)
+ setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
}
if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -311,12 +319,11 @@
*/
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- var amount = 1.0f
- if (statusBarStateController.state == StatusBarState.SHADE ||
- statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
- amount = 0.0f
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+ } else {
+ setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
- setDozeAmount(amount, amount)
return true
}
return false
@@ -332,7 +339,7 @@
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
- setDozeAmount(1f, 1f)
+ setDozeAmount(1f, 1f, source = "Override: animating screen off")
return true
}
@@ -426,4 +433,24 @@
*/
@JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
}
-}
\ No newline at end of file
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("mLinearDozeAmount: $mLinearDozeAmount")
+ pw.println("mDozeAmount: $mDozeAmount")
+ pw.println("mDozeAmountSource: $mDozeAmountSource")
+ pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
+ pw.println("mNotificationsVisible: $mNotificationsVisible")
+ pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
+ pw.println("mVisibilityAmount: $mVisibilityAmount")
+ pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+ pw.println("pulseExpanding: $pulseExpanding")
+ pw.println("state: ${StatusBarState.toString(state)}")
+ pw.println("fullyAwake: $fullyAwake")
+ pw.println("wakingUp: $wakingUp")
+ pw.println("willWakeUp: $willWakeUp")
+ pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
+ pw.println("pulsing: $pulsing")
+ pw.println("notificationsFullyHidden: $notificationsFullyHidden")
+ pw.println("canShowPulsingHuns: $canShowPulsingHuns")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a2ea0ba..a5bf88e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,7 +31,7 @@
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
+import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -171,6 +171,7 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -1089,7 +1090,6 @@
isFolded, willGoToSleep, isShadeOpen, leaveOpen));
}
if (leaveOpen) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
if (mKeyguardStateController.isShowing()) {
// When device state changes on keyguard we don't want to keep the state of
// the shade and instead we open clean state of keyguard with shade closed.
@@ -1098,6 +1098,9 @@
// expanded. To prevent that we can close QS which resets QS and some parts of
// the shade to its default state. Read more in b/201537421
mCloseQsBeforeScreenOff = true;
+ } else {
+ // below makes shade stay open when going from folded to unfolded
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
}
}
@@ -2192,7 +2195,8 @@
public void onAnimationEnded() {
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
- }, false, sUiEventLogger).show(animationDelay);
+ }, /* isDozing= */ false, RippleShape.CIRCLE,
+ sUiEventLogger).show(animationDelay);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 6dbbf0d..fc8e7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -30,7 +30,6 @@
import com.android.systemui.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -255,9 +254,7 @@
public void addMobileView(MobileIconState state) {
Log.d(TAG, "addMobileView: ");
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, state.slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
view.applyMobileState(state);
view.setStaticDrawableColor(mColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 31d9266..30b640b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,7 +38,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -361,9 +360,7 @@
}
private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
return view;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4d9c811..e9771d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -387,6 +388,7 @@
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
+ && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
&& !isUnlockCollapsing()) {
mBouncer.setExpansion(fraction);
@@ -398,9 +400,8 @@
}
} else if (!mShowing && mBouncer.inTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
- // We need to keep propagating the expansion state to the bouncer, otherwise it will be
- // stuck in transit.
- mBouncer.setExpansion(fraction);
+ // We need to hide the bouncer, otherwise it will be stuck in transit.
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -470,7 +471,8 @@
showBouncer(scrimmed);
}
- private boolean shouldShowAltAuth() {
+ /** Whether we should show the alternate authentication instead of the traditional bouncer. */
+ public boolean shouldShowAltAuth() {
return mAlternateAuthInterceptor != null
&& mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
}
@@ -739,8 +741,10 @@
}
mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
- // setDozing(false) will call reset once we stop dozing.
- if (!mDozing) {
+ // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
+ // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
+ // unexpected visible behavior if the keyguard is still visible as we're animating unlocked.
+ if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) {
// If Keyguard is reshown, don't hide the bouncer as it might just have been requested
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ee242a4..492734e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,8 +26,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -66,7 +64,6 @@
private final Handler mHandler = Handler.getMain();
private final CarrierConfigTracker mCarrierConfigTracker;
private final TunerService mTunerService;
- private final FeatureFlags mFeatureFlags;
private boolean mHideAirplane;
private boolean mHideMobile;
@@ -90,8 +87,7 @@
CarrierConfigTracker carrierConfigTracker,
NetworkController networkController,
SecurityController securityController,
- TunerService tunerService,
- FeatureFlags featureFlags
+ TunerService tunerService
) {
mContext = context;
@@ -100,7 +96,6 @@
mNetworkController = networkController;
mSecurityController = securityController;
mTunerService = tunerService;
- mFeatureFlags = featureFlags;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -378,40 +373,6 @@
}
@Override
- public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
- boolean noNetworksAvailable) {
- if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setConnectivityStatus: "
- + "noDefaultNetwork = " + noDefaultNetwork + ","
- + "noValidatedNetwork = " + noValidatedNetwork + ","
- + "noNetworksAvailable = " + noNetworksAvailable);
- }
- WifiIconState newState = mWifiIconState.copy();
- newState.noDefaultNetwork = noDefaultNetwork;
- newState.noValidatedNetwork = noValidatedNetwork;
- newState.noNetworksAvailable = noNetworksAvailable;
- newState.slot = mSlotWifi;
- newState.airplaneSpacerVisible = mIsAirplaneMode;
- if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_unavailable;
- } else if (noDefaultNetwork && !noNetworksAvailable
- && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_available;
- } else {
- newState.visible = false;
- newState.resId = 0;
- }
- updateWifiIconWithState(newState);
- mWifiIconState = newState;
- }
-
-
- @Override
public void setEthernetIndicators(IconState state) {
boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
new file mode 100644
index 0000000..6c02b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.statusbar.pipeline
+
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface exposing a flow for raw connectivity information. Clients should collect on
+ * [rawConnectivityInfoFlow] to get updates on connectivity information.
+ *
+ * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
+ * and all clients get references to the same flow.
+ *
+ * This will be used for the new status bar pipeline to compile information we need to display some
+ * of the icons in the RHS of the status bar.
+ */
+interface ConnectivityInfoCollector {
+ val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
+}
+
+/**
+ * An object containing all of the raw connectivity information.
+ *
+ * Importantly, all the information in this object should not be processed at all (i.e., the data
+ * that we receive from callbacks should be piped straight into this object and not be filtered,
+ * manipulated, or processed in any way). Instead, any listeners on
+ * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
+ *
+ * This allows us to keep all the processing in one place which is beneficial for logging and
+ * debugging purposes.
+ */
+data class RawConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
new file mode 100644
index 0000000..8d69422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
+ * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
+ */
+@SysUISingleton
+class ConnectivityInfoCollectorImpl @Inject constructor(
+ networkCapabilitiesRepo: NetworkCapabilitiesRepo,
+ @Application scope: CoroutineScope,
+) : ConnectivityInfoCollector {
+ override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
+ // TODO(b/238425913): Collect all the separate flows for individual raw information into
+ // this final flow.
+ networkCapabilitiesRepo.dataStream
+ .map {
+ RawConnectivityInfo(networkCapabilityInfo = it)
+ }
+ .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index a841914..1aae250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -19,7 +19,15 @@
import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@
*
* This will be used for the new status bar pipeline to calculate the list of icons that should be
* displayed in the RHS of the status bar.
+ *
+ * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
*/
@SysUISingleton
class ConnectivityInfoProcessor @Inject constructor(
+ connectivityInfoCollector: ConnectivityInfoCollector,
context: Context,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
+ @Application private val scope: CoroutineScope,
+ statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable(context) {
+ // Note: This flow will not start running until a client calls `collect` on it, which means that
+ // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
+ // happens.
+ val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
+ if (!statusBarPipelineFlags.isNewPipelineEnabled())
+ emptyFlow()
+ else connectivityInfoCollector.rawConnectivityInfoFlow
+ .map { it.process() }
+ .stateIn(
+ scope,
+ started = Lazily,
+ initialValue = ProcessedConnectivityInfo()
+ )
+
override fun start() {
- if (statusBarPipelineFlags.isNewPipelineEnabled()) {
- init()
- }
}
- /** Initializes this processor and everything it depends on. */
- private fun init() {
- // TODO(b/238425913): Register all the connectivity callbacks here.
+ private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
+ // TODO(b/238425913): Actually process the raw info into meaningful data.
+ return ProcessedConnectivityInfo(this.networkCapabilityInfo)
}
}
+
+/**
+ * An object containing connectivity info that has been processed into data that can be directly
+ * used by the status bar (and potentially other SysUI areas) to display icons.
+ */
+data class ProcessedConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 771bb0c..c4e2b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.dagger
import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import dagger.Binds
import dagger.Module
@@ -30,4 +32,9 @@
@IntoMap
@ClassKey(ConnectivityInfoProcessor::class)
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
+
+ @Binds
+ abstract fun provideConnectivityInfoCollector(
+ impl: ConnectivityInfoCollectorImpl
+ ): ConnectivityInfoCollector
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e22a896..4c76270 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -405,8 +405,8 @@
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
- BubblesManager.this.onEntryUpdated(entry);
+ public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ BubblesManager.this.onEntryUpdated(entry, fromSystem);
}
@Override
@@ -444,9 +444,10 @@
}
}
- void onEntryUpdated(NotificationEntry entry) {
+ void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
- mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+ shouldBubble, fromSystem);
}
void onEntryRemoved(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 12597e0..eba2795 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -118,6 +118,33 @@
private final UserInfoController mUserInfoController;
private final Executor mSysUiMainExecutor;
+ // Listeners and callbacks. Note that we prefer member variable over anonymous class here to
+ // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference
+ // internally and anonymous class could be released after registration.
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mShell.onConfigurationChanged(newConfig);
+ }
+ };
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
+ mKeyguardStateController.isOccluded(),
+ mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
+ }
+ };
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
+ mShell.onKeyguardDismissAnimationFinished();
+ }
+ };
+
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@@ -159,28 +186,11 @@
public void start() {
// Notify with the initial configuration and subscribe for new config changes
mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- mShell.onConfigurationChanged(newConfig);
- }
- });
+ mConfigurationController.addCallback(mConfigurationListener);
// Subscribe to keyguard changes
- mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
- mKeyguardStateController.isOccluded(),
- mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
- }
- });
- mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardDismissAnimationFinished() {
- mShell.onKeyguardDismissAnimationFinished();
- }
- });
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
// TODO: Consider piping config change and other common calls to a shell component to
// delegate internally
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index dffad6c..80385e6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -44,6 +44,7 @@
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
@@ -190,7 +191,7 @@
private void verifyViewDismissed(SurfaceView v) throws Exception {
verify(mKeyguardSecurityContainer).removeView(v);
- verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
+ verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true, SecurityMode.Invalid);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index ad6d146..bb455da 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -86,10 +86,11 @@
becauseCannotSkipBouncer = false,
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
+ onlyFaceEnrolled = false,
faceAuthenticated = false,
faceDisabled = false,
goingToSleep = false,
- keyguardAwake = false,
+ keyguardAwakeExcludingBouncerShowing = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
occludingAppRequestingFaceAuth = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index dc87a6a..aecec9d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -21,7 +21,10 @@
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -76,6 +79,8 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+ private static final int TARGET_USER_ID = 100;
+
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -381,6 +386,44 @@
verify(mSidefpsController, never()).show();
}
+ @Test
+ public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
+ // GIVEN the current security method is SimPin
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN);
+ mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ // THEN the next security method of PIN is set, and the keyguard is not marked as done
+ verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt());
+ assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode())
+ .isEqualTo(SecurityMode.PIN);
+ }
+
+ @Test
+ public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
+ //GIVEN current security mode has been set to PIN
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN);
+
+ //WHEN a request comes from SimPin to dismiss the security screens
+ boolean keyguardDone = mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ //THEN no action has happened, which will not dismiss the security screens
+ assertThat(keyguardDone).isEqualTo(false);
+ verify(mKeyguardUpdateMonitor, never()).getUserHasTrust(anyInt());
+ }
+
private void setupConditionsToEnableSideFpsHint() {
attachView();
setSideFpsHintEnabledFromResources(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 85ecfd3..c677371 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,11 +16,13 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.google.common.truth.Truth.assertThat;
@@ -31,7 +33,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -41,6 +42,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -52,6 +54,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +71,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.ServiceState;
@@ -106,6 +110,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.internal.util.reflection.FieldSetter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -182,26 +187,39 @@
private ActiveUnlockConfig mActiveUnlockConfig;
@Mock
private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
+ @Mock
+ private IActivityManager mActivityService;
+
+ private final int mCurrentUserId = 100;
+ private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
+
+ @Captor
+ private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
+ mBiometricEnabledCallbackArgCaptor;
+ @Captor
+ private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
+
// Direct executor
- private Executor mBackgroundExecutor = Runnable::run;
- private Executor mMainExecutor = Runnable::run;
+ private final Executor mBackgroundExecutor = Runnable::run;
+ private final Executor mMainExecutor = Runnable::run;
private TestableLooper mTestableLooper;
+ private Handler mHandler;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableContext mSpiedContext;
private MockitoSession mMockitoSession;
private StatusBarStateController.StateListener mStatusBarStateListener;
+ private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
@Before
- public void setup() {
+ public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
- doAnswer(invocation -> {
- IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
- callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
- return null;
- }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+ when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
+ when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +280,27 @@
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
+ KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
.when(ActivityManager::getCurrentUser);
+ ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+ verify(mBiometricManager)
+ .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
+ mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
+ biometricsEnabledForCurrentUser();
+
+ mHandler = spy(mKeyguardUpdateMonitor.getHandler());
+ try {
+ FieldSetter.setField(mKeyguardUpdateMonitor,
+ KeyguardUpdateMonitor.class.getDeclaredField("mHandler"), mHandler);
+ } catch (NoSuchFieldException e) {
+
+ }
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -308,7 +340,7 @@
when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
- when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+ when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId});
KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
@@ -483,7 +515,7 @@
// Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
- Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
+ Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
intentSimState.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -498,7 +530,7 @@
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
- anyInt());
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@@ -548,11 +580,12 @@
@Test
public void testTriesToAuthenticate_whenBouncer() {
+ fingerprintIsNotEnrolled();
setKeyguardBouncerVisibility(true);
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
- verify(mFaceManager).isHardwareDetected();
- verify(mFaceManager).hasEnrolledTemplates(anyInt());
+ verify(mFaceManager, atLeastOnce()).isHardwareDetected();
+ verify(mFaceManager, atLeastOnce()).hasEnrolledTemplates(anyInt());
}
@Test
@@ -718,7 +751,6 @@
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
-
@Test
public void testFaceAndFingerprintLockout() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -769,7 +801,8 @@
public void testBiometricsCleared_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
final BiometricAuthenticated dummyAuthentication =
new BiometricAuthenticated(true /* authenticated */, true /* strong */);
@@ -787,7 +820,8 @@
public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
@@ -924,6 +958,7 @@
@Test
public void testSecondaryLockscreenRequirement() {
+ KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
int user = KeyguardUpdateMonitor.getCurrentUser();
String packageName = "fake.test.package";
String cls = "FakeService";
@@ -1097,8 +1132,7 @@
@Test
public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
// GIVEN state for face auth should run aside from StatusBarState
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ biometricsNotDisabledThroughDevicePolicyManager();
mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
setKeyguardBouncerVisibility(false /* isVisible */);
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1187,471 @@
verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
}
+ @Test
+ public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
+ mFaceManager = null;
+ mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ // Fingerprint is locked out.
+ fingerprintErrorLockedOut();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ triggerSuccessfulFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+ // This disables face auth
+ when(mUserManager.isPrimaryUser()).thenReturn(false);
+ mKeyguardUpdateMonitor =
+ new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ // This disables face auth
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ // This disables face auth
+ biometricsDisabledForCurrentUser();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ userCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ secureCameraLaunched();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ secureCameraLaunched();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ occludingAppRequestsFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ triggerAuthInterrupt();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ bouncerFullyVisible();
+
+ statusBarShadeIsLocked();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ deviceNotGoingToSleep();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ deviceIsInteractive();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ keyguardIsVisible();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ statusBarShadeIsNotLocked();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ bouncerNotFullyVisible();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUdfpsBouncerIsShowing_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
+ throws RemoteException {
+ // Both fingerprint and face are enrolled by default
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ deviceNotGoingToSleep();
+ deviceIsInteractive();
+ statusBarShadeIsNotLocked();
+ keyguardIsVisible();
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ mKeyguardUpdateMonitor.requestFaceAuth(true);
+ verify(mFaceManager).authenticate(any(),
+ mCancellationSignalCaptor.capture(),
+ mAuthenticationCallbackCaptor.capture(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue();
+
+ bouncerFullyVisible();
+ mTestableLooper.processAllMessages();
+
+ assertThat(cancelSignal.isCanceled()).isTrue();
+ }
+
+ @Test
+ public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
+
+ mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+ // Make sure keyguard is going away after face auth attempt, and that it calls
+ // updateBiometricStateListeningState.
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
+ DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler, times(1)).removeCallbacks(mKeyguardUpdateMonitor.mFpCancelNotReceived);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true);
+ }
+
+ private void fingerprintIsNotEnrolled() {
+ when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
+ }
+
+ private void statusBarShadeIsNotLocked() {
+ mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+ }
+
+ private void statusBarShadeIsLocked() {
+ mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
+ }
+
+ private void keyguardIsVisible() {
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ }
+
+ private void triggerAuthInterrupt() {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
+ }
+
+ private void occludingAppRequestsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+ }
+
+ private void secureCameraLaunched() {
+ mKeyguardUpdateMonitor.onCameraLaunched();
+ }
+
+ private void userCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(true);
+ }
+
+ private void fingerprintErrorLockedOut() {
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
+ }
+
+ private void triggerSuccessfulFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuth(true);
+ verify(mFaceManager).authenticate(any(),
+ any(),
+ mAuthenticationCallbackCaptor.capture(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ mAuthenticationCallbackCaptor.getValue()
+ .onAuthenticationSucceeded(
+ new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
+ }
+
+ private void currentUserIsPrimary() {
+ when(mUserManager.isPrimaryUser()).thenReturn(true);
+ }
+
+ private void biometricsNotDisabledThroughDevicePolicyManager() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ }
+
+ private void biometricsEnabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ private void biometricsDisabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser()
+ );
+ }
+
+ private void strongAuthNotRequired() {
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(0);
+ }
+
+ private void currentUserDoesNotHaveTrust() {
+ mKeyguardUpdateMonitor.onTrustChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser(),
+ -1,
+ new ArrayList<>()
+ );
+ }
+
+ private void userNotCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(false);
+ }
+
+ private void keyguardNotGoingAway() {
+ mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
+ }
+
+ private void bouncerFullyVisibleAndNotGoingToSleep() {
+ bouncerFullyVisible();
+ deviceNotGoingToSleep();
+ }
+
+ private void deviceNotGoingToSleep() {
+ mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
+ }
+
+ private void deviceIsInteractive() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ }
+
+ private void bouncerNotFullyVisible() {
+ setKeyguardBouncerVisibility(false);
+ }
+
+ private void bouncerFullyVisible() {
+ setKeyguardBouncerVisibility(true);
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index c48cbb1..0f11241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -34,19 +35,18 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+ private val testLaunchAnimator = fakeLaunchAnimator()
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Mock lateinit var listener: ActivityLaunchAnimator.Listener
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -77,12 +77,13 @@
// We start in a new thread so that we can ensure that the callbacks are called in the main
// thread.
thread {
- animator.startIntentWithAnimation(
+ animator.startIntentWithAnimation(
controller = controller,
animate = animate,
intentStarter = intentStarter
- )
- }.join()
+ )
+ }
+ .join()
}
@Test
@@ -197,14 +198,25 @@
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
val taskInfo = ActivityManager.RunningTaskInfo()
taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
- taskInfo.topActivityInfo = ActivityInfo().apply {
- applicationInfo = ApplicationInfo()
- }
+ taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
return RemoteAnimationTarget(
- 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
- Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
- taskInfo, false
+ 0,
+ RemoteAnimationTarget.MODE_OPENING,
+ SurfaceControl(),
+ false,
+ Rect(),
+ Rect(),
+ 0,
+ Point(),
+ Rect(),
+ bounds,
+ WindowConfiguration(),
+ false,
+ SurfaceControl(),
+ Rect(),
+ taskInfo,
+ false
)
}
}
@@ -213,17 +225,17 @@
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
* outside of the main thread.
*/
-private class TestLaunchAnimatorController(
- override var launchContainer: ViewGroup
-) : ActivityLaunchAnimator.Controller {
- override fun createAnimatorState() = LaunchAnimator.State(
+private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) :
+ ActivityLaunchAnimator.Controller {
+ override fun createAnimatorState() =
+ LaunchAnimator.State(
top = 100,
bottom = 200,
left = 300,
right = 400,
topCornerRadius = 10f,
bottomCornerRadius = 20f
- )
+ )
private fun assertOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 4218e09..7c1e384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -5,7 +5,6 @@
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
-import android.service.dreams.IDreamManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -38,19 +37,16 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
- private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
- @Mock lateinit var dreamManager: IDreamManager
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setUp() {
- dialogLaunchAnimator = DialogLaunchAnimator(
- dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
- )
+ dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor)
}
@After
@@ -153,6 +149,22 @@
}
@Test
+ fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
+ fun testActivityLaunchWhenLockedWithAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
fun testDialogAnimationIsChangedByAnimator() {
// Important: the power menu animation relies on this behavior to know when to animate (see
// http://ag/16774605).
@@ -193,11 +205,13 @@
verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
}
- private fun createAndShowDialog(): TestDialog {
+ private fun createAndShowDialog(
+ animator: DialogLaunchAnimator = dialogLaunchAnimator,
+ ): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
- dialogLaunchAnimator.showFromView(dialog, touchSurface)
+ animator.showFromView(dialog, touchSurface)
dialog
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
deleted file mode 100644
index dadf94e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.systemui.animation
-
-/**
- * A [LaunchAnimator.Timings] to be used in tests.
- *
- * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
- * when computing the progress of a sub-animation (the contents fade in/out).
- */
-val TEST_TIMINGS = LaunchAnimator.Timings(
- totalDuration = 0L,
- contentBeforeFadeOutDelay = 1L,
- contentBeforeFadeOutDuration = 1L,
- contentAfterFadeInDelay = 1L,
- contentAfterFadeInDuration = 1L
-)
-
-/** A [LaunchAnimator.Interpolators] to be used in tests. */
-val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
- positionInterpolator = Interpolators.STANDARD,
- positionXInterpolator = Interpolators.STANDARD,
- contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
- contentAfterFadeInInterpolator = Interpolators.STANDARD
-)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index 2915f5a..e099c92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -20,6 +20,7 @@
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
@@ -60,6 +61,8 @@
.isEqualTo(COMPLICATION_TYPE_CAST_INFO);
assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS))
.isEqualTo(COMPLICATION_TYPE_HOME_CONTROLS);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_SMARTSPACE))
+ .isEqualTo(COMPLICATION_TYPE_SMARTSPACE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b2a4e33..7b1455c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -19,7 +19,6 @@
import android.util.SparseArray
import android.util.SparseBooleanArray
import androidx.core.util.containsKey
-import java.lang.IllegalStateException
class FakeFeatureFlags : FeatureFlags {
private val booleanFlags = SparseBooleanArray()
@@ -57,7 +56,10 @@
stringFlags.put(flag.id, value)
}
- override fun isEnabled(flag: BooleanFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 7d4b4f5..ff579a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -29,11 +29,12 @@
@RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
- private val booleanFlag = BooleanFlag(-1000)
- private val stringFlag = StringFlag(-1001)
- private val resourceBooleanFlag = ResourceBooleanFlag(-1002, resourceId = -1)
- private val resourceStringFlag = ResourceStringFlag(-1003, resourceId = -1)
- private val sysPropBooleanFlag = SysPropBooleanFlag(-1004, name = "test")
+ private val unreleasedFlag = UnreleasedFlag(-1000)
+ private val releasedFlag = ReleasedFlag(-1001)
+ private val stringFlag = StringFlag(-1002)
+ private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
+ private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
+ private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
/**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -49,56 +50,66 @@
assertThat(ex.message).contains("TEAMFOOD")
}
try {
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
assertThat(ex.message).contains("UNKNOWN(id=-1000)")
}
try {
- assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
- fail("Expected an exception when accessing an unspecified flag.")
- } catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1002)")
- }
- try {
- assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
- fail("Expected an exception when accessing an unspecified flag.")
- } catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1004)")
- }
- try {
- assertThat(flags.getString(stringFlag)).isEmpty()
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
assertThat(ex.message).contains("UNKNOWN(id=-1001)")
}
try {
- assertThat(flags.getString(resourceStringFlag)).isEmpty()
+ assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
assertThat(ex.message).contains("UNKNOWN(id=-1003)")
}
+ try {
+ assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
+ fail("Expected an exception when accessing an unspecified flag.")
+ } catch (ex: IllegalStateException) {
+ assertThat(ex.message).contains("UNKNOWN(id=-1005)")
+ }
+ try {
+ assertThat(flags.getString(stringFlag)).isEmpty()
+ fail("Expected an exception when accessing an unspecified flag.")
+ } catch (ex: IllegalStateException) {
+ assertThat(ex.message).contains("UNKNOWN(id=-1002)")
+ }
+ try {
+ assertThat(flags.getString(resourceStringFlag)).isEmpty()
+ fail("Expected an exception when accessing an unspecified flag.")
+ } catch (ex: IllegalStateException) {
+ assertThat(ex.message).contains("UNKNOWN(id=-1004)")
+ }
}
@Test
fun specifiedFlagsReturnCorrectValues() {
val flags = FakeFeatureFlags()
- flags.set(booleanFlag, false)
+ flags.set(unreleasedFlag, false)
+ flags.set(releasedFlag, false)
flags.set(resourceBooleanFlag, false)
flags.set(sysPropBooleanFlag, false)
flags.set(resourceStringFlag, "")
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
assertThat(flags.getString(resourceStringFlag)).isEmpty()
- flags.set(booleanFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.set(releasedFlag, true)
flags.set(resourceBooleanFlag, true)
flags.set(sysPropBooleanFlag, true)
flags.set(resourceStringFlag, "Android")
- assertThat(flags.isEnabled(booleanFlag)).isTrue()
+ assertThat(flags.isEnabled(unreleasedFlag)).isTrue()
+ assertThat(flags.isEnabled(releasedFlag)).isTrue()
assertThat(flags.isEnabled(resourceBooleanFlag)).isTrue()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 51f3404..4511193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -35,6 +35,10 @@
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -48,12 +52,8 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -75,10 +75,11 @@
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
- private val teamfoodableFlagA = BooleanFlag(500, false, true)
- private val teamfoodableFlagB = BooleanFlag(501, true, true)
+ private val teamfoodableFlagA = UnreleasedFlag(500, true)
+ private val teamfoodableFlagB = ReleasedFlag(501, true)
@Before
fun setup() {
@@ -93,6 +94,7 @@
resources,
dumpManager,
deviceConfig,
+ serverFlagReader,
flagMap,
commandRegistry,
barService
@@ -109,40 +111,41 @@
}
@Test
- fun testReadBooleanFlag() {
+ fun readBooleanFlag() {
// Remember that the TEAMFOOD flag is id#1 and has special behavior.
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(5, false))).isFalse()
+
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
}
@Test
- fun testTeamFoodFlag_False() {
+ fun teamFoodFlag_False() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_True() {
+ fun teamFoodFlag_True() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_Overridden() {
+ fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
@@ -153,11 +156,11 @@
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testReadResourceBooleanFlag() {
+ fun readResourceBooleanFlag() {
whenever(resources.getBoolean(1001)).thenReturn(false)
whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getBoolean(1003)).thenReturn(false)
@@ -182,7 +185,7 @@
}
@Test
- fun testReadSysPropBooleanFlag() {
+ fun readSysPropBooleanFlag() {
whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
if ("b".equals(it.getArgument<String?>(0))) {
return@thenAnswer true
@@ -198,7 +201,7 @@
}
@Test
- fun testReadDeviceConfigBooleanFlag() {
+ fun readDeviceConfigBooleanFlag() {
val namespace = "test_namespace"
deviceConfig.setProperty(namespace, "a", "true", false)
deviceConfig.setProperty(namespace, "b", "false", false)
@@ -213,7 +216,7 @@
}
@Test
- fun testReadStringFlag() {
+ fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
@@ -223,7 +226,7 @@
}
@Test
- fun testReadResourceStringFlag() {
+ fun readResourceStringFlag() {
whenever(resources.getString(1001)).thenReturn("")
whenever(resources.getString(1002)).thenReturn("resource2")
whenever(resources.getString(1003)).thenReturn("resource3")
@@ -253,8 +256,8 @@
}
@Test
- fun testBroadcastReceiverIgnoresInvalidData() {
- addFlag(BooleanFlag(1, false))
+ fun broadcastReceiver_IgnoresInvalidData() {
+ addFlag(UnreleasedFlag(1))
addFlag(ResourceBooleanFlag(2, 1002))
addFlag(StringFlag(3, "flag3"))
addFlag(ResourceStringFlag(4, 1004))
@@ -272,10 +275,10 @@
}
@Test
- fun testIntentWithIdButNoValueKeyClears() {
- addFlag(BooleanFlag(1, false))
+ fun intentWithId_NoValueKeyClears() {
+ addFlag(UnreleasedFlag(1))
- // trying to erase an id not in the map does noting
+ // trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
@@ -291,9 +294,9 @@
}
@Test
- fun testSetBooleanFlag() {
- addFlag(BooleanFlag(1, false))
- addFlag(BooleanFlag(2, false))
+ fun setBooleanFlag() {
+ addFlag(UnreleasedFlag(1))
+ addFlag(UnreleasedFlag(2))
addFlag(ResourceBooleanFlag(3, 1003))
addFlag(ResourceBooleanFlag(4, 1004))
@@ -311,7 +314,7 @@
}
@Test
- fun testSetStringFlag() {
+ fun setStringFlag() {
addFlag(StringFlag(1, "flag1"))
addFlag(ResourceStringFlag(2, 1002))
@@ -323,7 +326,7 @@
}
@Test
- fun testSetFlagClearsCache() {
+ fun setFlag_ClearsCache() {
val flag1 = addFlag(StringFlag(1, "flag1"))
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
@@ -345,12 +348,30 @@
}
@Test
- fun testRegisterCommand() {
+ fun serverSide_Overrides_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_Overrides_MakesTrue() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
+ }
+
+ @Test
+ fun statusBarCommand_IsRegistered() {
verify(commandRegistry).registerCommand(anyString(), any())
}
@Test
- fun testNoOpCommand() {
+ fun noOpCommand() {
val cmd = captureCommand()
cmd.execute(pw, ArrayList())
@@ -360,37 +381,73 @@
}
@Test
- fun testReadFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun readFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1"))
verify(flagManager).readFlagValue<Boolean>(eq(1), any())
}
@Test
- fun testSetFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun setFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "on"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
}
@Test
- fun testToggleFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun toggleFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "toggle"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
}
@Test
- fun testEraseFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun eraseFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "erase"))
verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
}
+ @Test
+ fun dumpFormat() {
+ val flag1 = ReleasedFlag(1)
+ val flag2 = ResourceBooleanFlag(2, 1002)
+ val flag3 = UnreleasedFlag(3)
+ val flag4 = StringFlag(4, "")
+ val flag5 = StringFlag(5, "flag5default")
+ val flag6 = ResourceStringFlag(6, 1006)
+ val flag7 = ResourceStringFlag(7, 1007)
+
+ whenever(resources.getBoolean(1002)).thenReturn(true)
+ whenever(resources.getString(1006)).thenReturn("resource1006")
+ whenever(resources.getString(1007)).thenReturn("resource1007")
+ whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
+ .thenReturn("override7")
+
+ // WHEN the flags have been accessed
+ assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
+ assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
+
+ // THEN the dump contains the flags and the default values
+ val dump = dumpToString()
+ assertThat(dump).contains(" sysui_flag_1: true\n")
+ assertThat(dump).contains(" sysui_flag_2: true\n")
+ assertThat(dump).contains(" sysui_flag_3: false\n")
+ assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
+ assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
+ assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
+ assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
+ }
+
private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
inOrder(flagManager, secureSettings).apply {
verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
@@ -421,42 +478,6 @@
return captor.value.invoke()
}
- @Test
- fun testDump() {
- val flag1 = BooleanFlag(1, true)
- val flag2 = ResourceBooleanFlag(2, 1002)
- val flag3 = BooleanFlag(3, false)
- val flag4 = StringFlag(4, "")
- val flag5 = StringFlag(5, "flag5default")
- val flag6 = ResourceStringFlag(6, 1006)
- val flag7 = ResourceStringFlag(7, 1007)
-
- whenever(resources.getBoolean(1002)).thenReturn(true)
- whenever(resources.getString(1006)).thenReturn("resource1006")
- whenever(resources.getString(1007)).thenReturn("resource1007")
- whenever(flagManager.readFlagValue(eq(7), eq(StringFlagSerializer)))
- .thenReturn("override7")
-
- // WHEN the flags have been accessed
- assertThat(mFeatureFlagsDebug.isEnabled(flag1)).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(flag2)).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(flag3)).isFalse()
- assertThat(mFeatureFlagsDebug.getString(flag4)).isEmpty()
- assertThat(mFeatureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
- assertThat(mFeatureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
- assertThat(mFeatureFlagsDebug.getString(flag7)).isEqualTo("override7")
-
- // THEN the dump contains the flags and the default values
- val dump = dumpToString()
- assertThat(dump).contains(" sysui_flag_1: true\n")
- assertThat(dump).contains(" sysui_flag_2: true\n")
- assertThat(dump).contains(" sysui_flag_3: false\n")
- assertThat(dump).contains(" sysui_flag_4: [length=0] \"\"\n")
- assertThat(dump).contains(" sysui_flag_5: [length=12] \"flag5default\"\n")
- assertThat(dump).contains(" sysui_flag_6: [length=12] \"resource1006\"\n")
- assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
- }
-
private fun dumpToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
@@ -464,4 +485,4 @@
pw.flush()
return sw.toString()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index 6b683f4..e94b520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -30,8 +30,8 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -44,13 +44,18 @@
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mDumpManager: DumpManager
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig,
+ mFeatureFlagsRelease = FeatureFlagsRelease(
+ mResources,
+ mSystemProperties,
+ deviceConfig,
+ serverFlagReader,
mDumpManager)
}
@@ -113,4 +118,22 @@
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
-}
\ No newline at end of file
+
+ @Test
+ fun serverSide_OverridesReleased_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_OverridesUnreleased_Ignored() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index a2eca81..17324a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
@@ -33,9 +34,8 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -64,14 +64,14 @@
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing
- mFlagManager.addListener(BooleanFlag(2, true), listener2)
+ mFlagManager.addListener(ReleasedFlag(2), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction
- mFlagManager.addListener(BooleanFlag(1, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -101,8 +101,8 @@
fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -127,8 +127,8 @@
fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@
@Test
fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener)
- mFlagManager.addListener(BooleanFlag(10, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
+ mFlagManager.addListener(ReleasedFlag(10), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(1)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
- mFlagManager.addListener(BooleanFlag(10, true)) {
+ mFlagManager.addListener(ReleasedFlag(10)) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -295,4 +295,4 @@
assertThat(StringFlagSerializer.toSettingsData("foo"))
.isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
index 25c3028..250cc48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -107,11 +107,11 @@
}
private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new BooleanFlag(0);
- public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+ public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
+ public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
public static final StringFlag C_FLAG = new StringFlag(0);
- public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+ public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
public static final DoubleFlag E_FLAG = new DoubleFlag(3);
public static final DoubleFlag F_FLAG = new DoubleFlag(3);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index bcc76ab..810c6dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.keyguard.data.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -22,11 +22,10 @@
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
@@ -50,18 +49,19 @@
companion object {
@Parameters(
name =
- "feature enabled = {0}, has favorites = {1}, has service infos = {2} - expected" +
- " visible = {3}"
+ "feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
+ " while locked = {3} - expected visible = {4}"
)
@JvmStatic
fun data() =
- (0 until 8)
+ (0 until 16)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b100 != 0,
- /* hasFavorites= */ combination and 0b010 != 0,
- /* hasServiceInfos= */ combination and 0b001 != 0,
- /* isVisible= */ combination == 0b111,
+ /* isFeatureEnabled= */ combination and 0b1000 != 0,
+ /* hasFavorites= */ combination and 0b0100 != 0,
+ /* hasServiceInfos= */ combination and 0b0010 != 0,
+ /* canShowWhileLocked= */ combination and 0b0001 != 0,
+ /* isVisible= */ combination == 0b1111,
)
}
.toList()
@@ -79,7 +79,8 @@
@JvmField @Parameter(0) var isFeatureEnabled: Boolean = false
@JvmField @Parameter(1) var hasFavorites: Boolean = false
@JvmField @Parameter(2) var hasServiceInfos: Boolean = false
- @JvmField @Parameter(3) var isVisible: Boolean = false
+ @JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
+ @JvmField @Parameter(4) var isVisible: Boolean = false
@Before
fun setUp() {
@@ -89,6 +90,8 @@
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController())
.thenReturn(Optional.of(controlsListingController))
+ whenever(component.canShowWhileLockedSetting)
+ .thenReturn(MutableStateFlow(canShowWhileLocked))
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -111,14 +114,16 @@
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
val job = underTest.state.onEach(values::add).launchIn(this)
- verify(controlsListingController).addCallback(callbackCaptor.capture())
- callbackCaptor.value.onServicesUpdated(
- if (hasServiceInfos) {
- listOf(mock())
- } else {
- emptyList()
- }
- )
+ if (canShowWhileLocked) {
+ verify(controlsListingController).addCallback(callbackCaptor.capture())
+ callbackCaptor.value.onServicesUpdated(
+ if (hasServiceInfos) {
+ listOf(mock())
+ } else {
+ emptyList()
+ }
+ )
+ }
assertThat(values.last())
.isInstanceOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 592e80b..ef588f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -51,6 +51,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -60,7 +61,26 @@
}
@Test
- fun `state - when listing controller is missing - returns None`() = runBlockingTest {
+ fun `state - when cannot show while locked - returns Hidden`() = runBlockingTest {
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
+ whenever(component.isEnabled()).thenReturn(true)
+ whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
+ whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
+ val controlsController = mock<ControlsController>()
+ whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
+ whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
+
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
+ val job = underTest.state.onEach(values::add).launchIn(this)
+
+ assertThat(values.last())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.State.Hidden::class.java)
+ job.cancel()
+ }
+
+ @Test
+ fun `state - when listing controller is missing - returns Hidden`() = runBlockingTest {
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 4abb973..56aff3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -24,16 +24,11 @@
@Before
fun setup() {
outputWriter = StringWriter()
- buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH)
+ buffer = createBuffer()
}
- private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer {
- return LogBuffer("TestBuffer",
- 1,
- logcatEchoTracker,
- false,
- rootStackTraceDepth = rootTraceDepth,
- nestedStackTraceDepth = nestedTraceDepth)
+ private fun createBuffer(): LogBuffer {
+ return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
}
@Test
@@ -56,95 +51,83 @@
}
@Test
- fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1)
- // stack trace depth of 5
- val exception = createTestException("Exception message", "TestClass", 5)
+ fun dump_writesExceptionAndStacktrace() {
+ buffer = createBuffer()
+ val exception = createTestException("Exception message", "TestClass")
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
- assertThat(dumpedString).contains("E Tag: Extra message")
- assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
- assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ assertThat(dumpedString).contains("Extra message")
+ assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesCauseAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesCauseAndStacktrace() {
+ buffer = createBuffer()
val exception = createTestException("Exception message",
"TestClass",
- 1,
- cause = createTestException("The real cause!", "TestClass", 5))
+ cause = createTestException("The real cause!", "TestClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
assertThat(dumpedString)
- .contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
- assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: The real cause!")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesSuppressedExceptionAndStacktrace() {
+ buffer = createBuffer()
val exception = RuntimeException("Root exception message")
exception.addSuppressed(
createTestException(
"First suppressed exception",
"FirstClass",
- 5,
- createTestException("Cause of suppressed exp", "ThirdClass", 5)
+ createTestException("Cause of suppressed exp", "ThirdClass")
))
exception.addSuppressed(
- createTestException("Second suppressed exception", "SecondClass", 5))
+ createTestException("Second suppressed exception", "SecondClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedStr = dumpBuffer()
- // logs are limited to depth 2
// first suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: First suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
assertThat(dumpedStr)
- .contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
// second suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: Second suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
}
private fun createTestException(
- message: String,
- errorClass: String,
- stackTraceLength: Int,
- cause: Throwable? = null
+ message: String,
+ errorClass: String,
+ cause: Throwable? = null,
): Exception {
val exception = RuntimeException(message, cause)
- exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength)
+ exception.stackTrace = (1..5).map { lineNumber ->
+ StackTraceElement(errorClass,
+ "TestMethod",
+ "$errorClass.java",
+ lineNumber)
+ }.toTypedArray()
return exception
}
@@ -152,16 +135,4 @@
buffer.dump(PrintWriter(outputWriter), tailLength = 100)
return outputWriter.toString()
}
-
- private fun createStackTraceElements(
- errorClass: String,
- stackTraceLength: Int
- ): Array<StackTraceElement> {
- return (1..stackTraceLength).map { lineNumber ->
- StackTraceElement(errorClass,
- "TestMethod",
- "$errorClass.java",
- lineNumber)
- }.toTypedArray()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 6a532d7..6468fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -29,18 +29,18 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
@@ -433,7 +433,7 @@
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(100), eq(true))
- assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
// Smartspace update shouldn't be propagated for the empty rec list.
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 3d3ac83..83168cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -305,7 +305,7 @@
// Then we save an update with the current time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
- ?.dropLastWhile { it.isEmpty() }.forEach {
+ .dropLastWhile { it.isEmpty() }.forEach {
val result = it.split("/")
assertThat(result.size).isEqualTo(3)
assertThat(result[2].toLong()).isEqualTo(currentTime)
@@ -392,7 +392,7 @@
// Then we store the new lastPlayed time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
- ?.dropLastWhile { it.isEmpty() }.forEach {
+ .dropLastWhile { it.isEmpty() }.forEach {
val result = it.split("/")
assertThat(result.size).isEqualTo(3)
assertThat(result[2].toLong()).isEqualTo(currentTime)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 568e0cb..260bb87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -254,4 +254,30 @@
verify(mMediaOutputController).connectDevice(mMediaDevice2);
}
+
+ @Test
+ public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
+ when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+ List<MediaDevice> selectableDevices = new ArrayList<>();
+ selectableDevices.add(mMediaDevice1);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ mViewHolder.mContainerLayout.performClick();
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index edcf479..8073103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -112,7 +113,7 @@
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(CentralSurfaces.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index fe5f433..51f0953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -193,6 +194,8 @@
@Mock
private CentralSurfaces mCentralSurfaces;
@Mock
+ private KeyguardViewController mKeyguardViewController;
+ @Mock
private UserContextProvider mUserContextProvider;
@Mock
private Resources mResources;
@@ -237,8 +240,8 @@
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
- mock(NavigationModeController.class), mock(UserTracker.class),
- mock(DumpManager.class)));
+ mKeyguardViewController, mock(NavigationModeController.class),
+ mock(UserTracker.class), mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -377,7 +380,7 @@
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(true).when(mKeyguardViewController).isShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 07c8af9..be14cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -46,10 +45,10 @@
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -162,7 +161,6 @@
@Test
fun testRSSISlot_notCombined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
controller.init()
val captor = argumentCaptor<List<String>>()
@@ -174,20 +172,6 @@
}
@Test
- fun testRSSISlot_combined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
- controller.init()
-
- val captor = argumentCaptor<List<String>>()
- verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
-
- assertThat(captor.value).containsExactly(
- mContext.getString(com.android.internal.R.string.status_bar_no_calling),
- mContext.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- }
-
- @Test
fun testSingleCarrierCallback() {
controller.init()
reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 09ce37b..1e7722a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -44,7 +44,6 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -88,7 +87,6 @@
@Mock
private QSCarrier mQSCarrier3;
private TestableLooper mTestableLooper;
- @Mock private FeatureFlags mFeatureFlags;
@Mock
private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
@@ -130,7 +128,7 @@
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
- mFeatureFlags, mSlotIndexResolver)
+ mSlotIndexResolver)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 5212255..99a17a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,13 +22,11 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
import android.view.View;
import androidx.test.filters.SmallTest;
-import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -59,14 +57,14 @@
@Test
public void testUpdateState_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
}
@Test
public void testUpdateState_same() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
assertFalse(mQSCarrier.updateState(c, false));
@@ -74,7 +72,7 @@
@Test
public void testUpdateState_changed() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
@@ -85,14 +83,14 @@
@Test
public void testUpdateState_singleCarrier_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, true));
}
@Test
public void testUpdateState_singleCarrier_noShowIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
@@ -101,7 +99,7 @@
@Test
public void testUpdateState_multiCarrier_showIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, false);
@@ -110,7 +108,7 @@
@Test
public void testUpdateState_changeSingleMultiSingle() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 5336ef0..ba49f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -78,6 +79,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -144,7 +146,25 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.click(null /* view */);
- verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileClick(eq(SPEC), eq(StatusBarState.SHADE), eq(Tile.STATE_ACTIVE),
+ anyInt());
+ }
+
+ @Test
+ public void testHandleClick_log() {
+ mTile.click(null);
+ mTile.click(null);
+ mTestableLooper.processAllMessages();
+ mTile.click(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 2);
}
@Test
@@ -183,7 +203,25 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.secondaryClick(null /* view */);
- verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileSecondaryClick(eq(SPEC), eq(StatusBarState.SHADE),
+ eq(Tile.STATE_ACTIVE), anyInt());
+ }
+
+ @Test
+ public void testHandleSecondaryClick_log() {
+ mTile.secondaryClick(null);
+ mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
+ mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 2);
}
@Test
@@ -210,7 +248,25 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.longClick(null /* view */);
- verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileLongClick(eq(SPEC), eq(StatusBarState.SHADE),
+ eq(Tile.STATE_ACTIVE), anyInt());
+ }
+
+ @Test
+ public void testHandleLongClick_log() {
+ mTile.longClick(null);
+ mTile.longClick(null);
+ mTestableLooper.processAllMessages();
+ mTile.longClick(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 2);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
new file mode 100644
index 0000000..ce3f20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test the logic within ImageCaptureImpl
+ */
+@RunWith(AndroidTestingRunner::class)
+class ImageCaptureImplTest : SysuiTestCase() {
+ private val displayManager = mock<DisplayManager>()
+ private val atmService = mock<IActivityTaskManager>()
+ private val capture = TestableImageCaptureImpl(displayManager, atmService)
+
+ @Test
+ fun captureDisplayWithCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4))
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(2)
+ assertThat(capture.height!!).isEqualTo(2)
+ assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4))
+ }
+
+ @Test
+ fun captureDisplayWithNullCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, null)
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(0)
+ assertThat(capture.height!!).isEqualTo(0)
+ assertThat(capture.crop!!).isEqualTo(Rect())
+ }
+
+ class TestableImageCaptureImpl(
+ displayManager: DisplayManager,
+ atmService: IActivityTaskManager
+ ) :
+ ImageCaptureImpl(displayManager, atmService) {
+
+ var token: IBinder? = null
+ var width: Int? = null
+ var height: Int? = null
+ var crop: Rect? = null
+
+ override fun physicalDisplayToken(displayId: Int): IBinder {
+ return Binder()
+ }
+
+ override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect):
+ ScreenshotHardwareBuffer {
+ this.token = displayToken
+ this.width = width
+ this.height = height
+ this.crop = crop
+ return ScreenshotHardwareBuffer(null, null, false, false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
new file mode 100644
index 0000000..002f23a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.net.Uri
+import android.view.WindowManager
+import android.view.WindowManager.ScreenshotSource
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
+import org.junit.Test
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.isNull
+
+class RequestProcessorTest {
+ private val controller = mock<ScreenshotController>()
+ private val bitmapCaptor = argumentCaptor<Bitmap>()
+
+ @Test
+ fun testFullScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+ val processor = RequestProcessor(controller)
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
+ request, callback)
+
+ verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testSelectedRegionScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+ val processor = RequestProcessor(controller)
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
+ request, callback)
+
+ verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testProvidedImageScreenshot() {
+ val taskId = 1111
+ val userId = 2222
+ val bounds = Rect(50, 50, 150, 150)
+ val topComponent = ComponentName("test", "test")
+ val processor = RequestProcessor(controller)
+
+ val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+ val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
+ bounds, Insets.NONE, taskId, userId, topComponent)
+
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
+ request, callback)
+
+ verify(controller).handleImageAsScreenshot(
+ bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
+ eq(topComponent), eq(onSavedListener), eq(callback)
+ )
+
+ assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+ }
+
+ private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+ return bitmap.hardwareBuffer == this.hardwareBuffer &&
+ bitmap.colorSpace == this.colorSpace
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 3efdf2e..fc28349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,6 +47,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -461,6 +462,7 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
+ mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor),
@@ -902,6 +904,76 @@
}
@Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isTrue();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_pulsing_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_singleShade_isCentered() {
+ enableSplitShade(/* enabled= */ false);
+ // The conditions below would make the clock NOT be centered on split shade.
+ // On single shade it should always be centered though.
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
givenViewAttached();
when(mResources.getBoolean(
@@ -1473,4 +1545,19 @@
.thenReturn(splitShadeFullTransitionDistance);
mNotificationPanelViewController.updateResources();
}
+
+ private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+ mNotificationPanelViewController.setDozing(
+ /* dozing= */ dozing,
+ /* animate= */ false,
+ /* wakeUpTouchLocation= */ new PointF()
+ );
+ }
+
+ private boolean isKeyguardStatusViewCentered() {
+ mNotificationPanelViewController.updateResources();
+ return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
+ == ConstraintSet.PARENT_ID;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 185a838..6b34ca7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
@@ -27,13 +27,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,18 +42,17 @@
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.atLeast
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
-class LockscreenTargetFilterTest : SysuiTestCase() {
+class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
@Mock
private lateinit var secureSettings: SecureSettings
@@ -99,7 +99,7 @@
`when`(secureSettings
.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
.thenReturn(0)
- var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
contentResolver, uiExecution)
filter.addListener(listener)
@@ -132,7 +132,7 @@
`when`(secureSettings
.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
.thenReturn(0)
- var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
contentResolver, uiExecution)
filter.addListener(listener)
@@ -146,4 +146,4 @@
verify(listener).onCriteriaChanged()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 0d1879c..f8a0d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,8 +70,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -127,8 +125,8 @@
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
- protected FeatureFlags mFeatureFlags;
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
+ protected MobileSignalControllerFactory mMobileFactory;
protected int mSubId;
@@ -158,9 +156,6 @@
@Before
public void setUp() throws Exception {
- mFeatureFlags = mock(FeatureFlags.class);
- when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
-
mInstrumentation = InstrumentationRegistry.getInstrumentation();
Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
TestableResources res = mContext.getOrCreateTestableResources();
@@ -224,6 +219,11 @@
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+ mMobileFactory = new MobileSignalControllerFactory(
+ mContext,
+ mCallbackHandler,
+ mCarrierConfigTracker
+ );
mNetworkController = new NetworkControllerImpl(mContext,
mMockCm,
@@ -243,8 +243,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -438,10 +438,6 @@
updateSignalStrength();
}
- public void setImsType(int imsType) {
- mMobileSignalController.setImsType(imsType);
- }
-
public void setIsGsm(boolean gsm) {
when(mSignalStrength.isGsm()).thenReturn(gsm);
updateSignalStrength();
@@ -637,5 +633,4 @@
protected void assertDataNetworkNameEquals(String expected) {
assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index e3dd6f4..ed8a3e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
new Handler(TestableLooper.get(this).getLooper()),
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 698899a..a76676e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -121,8 +121,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
@@ -192,8 +192,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
mNetworkController.registerListeners();
@@ -277,8 +277,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 3f71491..68170ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -30,7 +30,6 @@
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.telephony.CellSignalStrength;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -285,44 +284,6 @@
verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
}
- @Test
- public void testCallStrengh() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
- setWifiLevel(testLevel);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
- }
- // Set the ImsType to be IMS_TYPE_WWAN
- setImsType(1);
- setupDefaultSignal();
- for (int testStrength = 0;
- testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
- setLevel(testStrength);
- verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
- }
- }
-
- @Test
- public void testNonPrimaryWiFi() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- when(mWifiInfo.isPrimary()).thenReturn(false);
- setWifiLevel(3);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- }
-
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 2a3509c..7b7f45a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -965,6 +965,7 @@
@Test
public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
@@ -975,6 +976,19 @@
}
@Test
+ public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+ }
+
+
+ @Test
public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1046dbc..2dcdcfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -202,12 +202,12 @@
}
@Test
- public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() {
+ public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
mStatusBarKeyguardViewManager.hide(0, 0);
when(mBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction()));
+ verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
@@ -249,6 +249,23 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
+ // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+ // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+ // which would mistakenly cause the bouncer to show briefly before its visibility
+ // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+ // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
new file mode 100644
index 0000000..515a7c9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.statusbar.pipeline
+
+import android.net.NetworkCapabilities
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(InternalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConnectivityInfoProcessorTest : SysuiTestCase() {
+
+ private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
+
+ @Before
+ fun setUp() {
+ whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
+ }
+
+ @Test
+ fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
+ // GIVEN a processor hooked up to a collector
+ val scope = CoroutineScope(Dispatchers.Unconfined)
+ val collector = FakeConnectivityInfoCollector()
+ val processor = ConnectivityInfoProcessor(
+ collector,
+ context,
+ scope,
+ statusBarPipelineFlags,
+ )
+
+ var mostRecentValue: ProcessedConnectivityInfo? = null
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ processor.processedInfoFlow.collect {
+ mostRecentValue = it
+ }
+ }
+
+ // WHEN the collector emits a value
+ val networkCapabilityInfo = mapOf(
+ 10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
+ )
+ collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
+ // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
+ // this test. So, our test needs to yield to let the job run.
+ // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
+ yield()
+
+ // THEN the processor receives it
+ assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
+
+ job.cancel()
+ scope.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
new file mode 100644
index 0000000..710e5f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.statusbar.pipeline
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
+ * receives in [emitValue].
+ */
+class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
+ private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
+ override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+
+ suspend fun emitValue(value: RawConnectivityInfo) {
+ _rawConnectivityInfoFlow.emit(value)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fee17c7..18acf3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
// Nothing should have changed
// Notif is suppressed after expansion
@@ -789,7 +790,7 @@
@Test
public void testAddNotif_notBubble() {
mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
- mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
assertThat(mBubbleController.hasBubbles()).isFalse();
}
@@ -827,7 +828,7 @@
NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking);
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
}
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it, it stays in the overflow but the entry is updated.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is active, and a non-interruptive notification update comes in for
+ * it, it doesn't trigger a new inflate and add for that bubble.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntTriggerInflate() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it with FLAG_BUBBLE that the flag is removed.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+ // Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
+ Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+
+ // Send a non-notifying update that has FLAG_BUBBLE
+ mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
+ assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
+ b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+ }
+
+ @Test
+ public void testNonSystemUpdatesIgnored() {
+ mEntryListener.onEntryAdded(mRow);
+ assertThat(mBubbleController.hasBubbles()).isTrue();
+
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c52ea60f..c83189d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
@@ -119,6 +122,7 @@
// is missing (constructing the actual one would throw).
// TODO(b/219008720): Remove this.
mDependency.injectMockDependency(SystemUIDialogManager.class);
+ mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
}
@After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
new file mode 100644
index 0000000..990db77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+import com.android.internal.jank.InteractionJankMonitor
+import org.mockito.Mockito.mock
+
+/** A [DialogLaunchAnimator] to be used in tests. */
+@JvmOverloads
+fun fakeDialogLaunchAnimator(
+ isUnlocked: Boolean = true,
+ isShowingAlternateAuthOnUnlock: Boolean = false,
+ interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+): DialogLaunchAnimator {
+ return DialogLaunchAnimator(
+ FakeCallback(
+ isUnlocked = isUnlocked,
+ isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+ ),
+ interactionJankMonitor,
+ fakeLaunchAnimator(),
+ isForTesting = true,
+ )
+}
+
+private class FakeCallback(
+ private val isDreaming: Boolean = false,
+ private val isUnlocked: Boolean = true,
+ private val isShowingAlternateAuthOnUnlock: Boolean = false,
+) : DialogLaunchAnimator.Callback {
+ override fun isDreaming(): Boolean = isDreaming
+ override fun isUnlocked(): Boolean = isUnlocked
+ override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
new file mode 100644
index 0000000..5b431e7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.animation
+
+/** A [LaunchAnimator] to be used in tests. */
+fun fakeLaunchAnimator(): LaunchAnimator {
+ return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+}
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+private val TEST_TIMINGS =
+ LaunchAnimator.Timings(
+ totalDuration = 0L,
+ contentBeforeFadeOutDelay = 1L,
+ contentBeforeFadeOutDuration = 1L,
+ contentAfterFadeInDelay = 1L,
+ contentAfterFadeInDuration = 1L
+ )
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+private val TEST_INTERPOLATORS =
+ LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.STANDARD,
+ positionXInterpolator = Interpolators.STANDARD,
+ contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+ contentAfterFadeInInterpolator = Interpolators.STANDARD
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 9b11a68..a185b58 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1115,10 +1115,7 @@
registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
saveGroupStateAsync(userId);
-
- if (DEBUG) {
- Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
- }
+ Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
}
return true;
@@ -4251,6 +4248,10 @@
IAppWidgetHost callbacks;
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+ private static final boolean DEBUG = true;
+
+ private static final String TAG = "AppWidgetServiceHost";
+
int tag = TAG_UNDEFINED; // for use while saving state (the index)
// Sequence no for the last update successfully sent. This is updated whenever a
// widget update is successfully sent to the host callbacks. As all new/undelivered updates
@@ -4321,6 +4322,11 @@
final SparseArray<String> uids = new SparseArray<>();
for (int i = widgets.size() - 1; i >= 0; i--) {
final Widget widget = widgets.get(i);
+ if (widget.provider == null) {
+ if (DEBUG) {
+ Slog.e(TAG, "Widget with no provider " + widget.toString());
+ }
+ }
final ProviderId providerId = widget.provider.id;
uids.put(providerId.uid, providerId.componentName.getPackageName());
}
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 35e9060..b255188 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -56,6 +56,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -208,6 +209,10 @@
}
@VisibleForTesting
+ VirtualDeviceManagerInternal getLocalServiceInstance() {
+ return mLocalService;
+ }
+
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -308,10 +313,11 @@
final long tokenTwo = Binder.clearCallingIdentity();
try {
virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
- return displayId;
} finally {
Binder.restoreCallingIdentity(tokenTwo);
}
+ mLocalService.onVirtualDisplayCreated(displayId);
+ return displayId;
}
@Nullable
@@ -378,6 +384,10 @@
}
private final class LocalService extends VirtualDeviceManagerInternal {
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
+ mVirtualDisplayListeners = new ArrayList<>();
+
@Override
public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
synchronized (mVirtualDeviceManagerLock) {
@@ -386,10 +396,30 @@
}
@Override
+ public void onVirtualDisplayCreated(int displayId) {
+ final VirtualDisplayListener[] listeners;
+ synchronized (mVirtualDeviceManagerLock) {
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+ }
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayCreated(displayId);
+ }
+ });
+ }
+
+ @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+ final VirtualDisplayListener[] listeners;
synchronized (mVirtualDeviceManagerLock) {
((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
}
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayRemoved(displayId);
+ }
+ });
}
@Override
@@ -435,6 +465,22 @@
}
return false;
}
+
+ @Override
+ public void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.remove(listener);
+ }
+ }
}
private static final class PendingTrampolineMap {
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index c9a420e..32dc470 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -138,6 +138,12 @@
INetd netd, int userId) {
return new Vpn(looper, context, nms, netd, userId, new VpnProfileStore());
}
+
+ /** Create a LockDownVpnTracker. */
+ public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
+ Vpn vpn, VpnProfile profile) {
+ return new LockdownVpnTracker(context, handler, vpn, profile);
+ }
}
public VpnManagerService(Context context, Dependencies deps) {
@@ -502,8 +508,7 @@
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(
- new LockdownVpnTracker(mContext, mHandler, vpn, profile));
+ setLockdownTracker(mDeps.createLockDownVpnTracker(mContext, mHandler, vpn, profile));
}
return true;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index ef784bb..5393b2a 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -160,6 +160,7 @@
public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
"android.hardware.biometrics.face.IFace/",
"android.hardware.biometrics.fingerprint.IFingerprint/",
+ "android.hardware.graphics.composer3.IComposer/",
"android.hardware.input.processor.IInputProcessor/",
"android.hardware.light.ILights/",
"android.hardware.power.IPower/",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 002aa77..e7ea903 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3595,11 +3595,12 @@
/**
* Bump the given service record into executing state.
- * @param oomAdjReason The caller requests it to perform the oomAdjUpdate if it's not null.
+ * @param oomAdjReason The caller requests it to perform the oomAdjUpdate not {@link
+ * OomAdjuster#OOM_ADJ_REASON_NONE}.
* @return {@code true} if it performed oomAdjUpdate.
*/
- private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
- @Nullable String oomAdjReason) {
+ private boolean bumpServiceExecutingLocked(
+ ServiceRecord r, boolean fg, String why, @OomAdjuster.OomAdjReason int oomAdjReason) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
@@ -3651,7 +3652,7 @@
}
}
boolean oomAdjusted = false;
- if (oomAdjReason != null && r.app != null
+ if (oomAdjReason != OomAdjuster.OOM_ADJ_REASON_NONE && r.app != null
&& r.app.mState.getCurProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
// Force an immediate oomAdjUpdate, so the client app could be in the correct process
// state before doing any service related transactions
@@ -4385,7 +4386,7 @@
final ProcessServiceRecord psr = app.mServices;
final boolean newService = psr.startService(r);
- bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
+ bumpServiceExecutingLocked(r, execInFg, "create", OomAdjuster.OOM_ADJ_REASON_NONE);
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(psr, /* oomAdj= */ false);
// Force an immediate oomAdjUpdate, so the client app could be in the correct process state
@@ -4511,7 +4512,7 @@
mAm.grantImplicitAccess(r.userId, si.intent, si.callingId,
UserHandle.getAppId(r.appInfo.uid)
);
- bumpServiceExecutingLocked(r, execInFg, "start", null /* oomAdjReason */);
+ bumpServiceExecutingLocked(r, execInFg, "start", OomAdjuster.OOM_ADJ_REASON_NONE);
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
if (DEBUG_BACKGROUND_CHECK) {
@@ -4780,7 +4781,7 @@
updateServiceForegroundLocked(r.app.mServices, false);
try {
oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
- oomAdjusted ? null : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ oomAdjusted ? 0 : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
mDestroyingServices.add(r);
r.destroying = true;
r.app.getThread().scheduleStopService(r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5729a06..7f1cf03 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8894,9 +8894,9 @@
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- // Time out after 10s, but kill logcat with SEGV
+ // Time out after 10s of inactivity, but kill logcat with SEGV
// so we can investigate why it didn't finish.
- "/system/bin/timeout", "-s", "SEGV", "10s",
+ "/system/bin/timeout", "-i", "-s", "SEGV", "10s",
// Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
@@ -14692,6 +14692,18 @@
}
}
+ if (!Build.IS_DEBUGGABLE && callingUid != ROOT_UID && callingUid != SHELL_UID
+ && callingUid != SYSTEM_UID) {
+ // If it's not debug build and not called from root/shell/system uid, reject it.
+ final String msg = "Permission Denial: instrumentation test "
+ + className + " from pid=" + callingPid + ", uid=" + callingUid
+ + ", pkgName=" + mInternal.getPackageNameByPid(callingPid)
+ + " not allowed because it's not started from SHELL";
+ Slog.wtfQuiet(TAG, msg);
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
+ throw new SecurityException(msg);
+ }
+
boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
@@ -15598,7 +15610,7 @@
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("this")
- void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ void updateOomAdjPendingTargetsLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
}
@@ -15617,7 +15629,7 @@
}
@GuardedBy("this")
- final void updateOomAdjLocked(String oomAdjReason) {
+ final void updateOomAdjLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
mOomAdjuster.updateOomAdjLocked(oomAdjReason);
}
@@ -15629,7 +15641,8 @@
* @return whether updateOomAdjLocked(app) was successful.
*/
@GuardedBy("this")
- final boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ final boolean updateOomAdjLocked(
+ ProcessRecord app, @OomAdjuster.OomAdjReason int oomAdjReason) {
return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
}
@@ -15862,14 +15875,16 @@
mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
}
- private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
+ private void trimApplications(
+ boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
synchronized (this) {
trimApplicationsLocked(forceFullOomAdj, oomAdjReason);
}
}
@GuardedBy("this")
- private void trimApplicationsLocked(boolean forceFullOomAdj, String oomAdjReason) {
+ private void trimApplicationsLocked(
+ boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
// First remove any unused application processes whose package
// has been removed.
boolean didSomething = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6609d4a..dbabe99 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -989,26 +989,40 @@
@NeverCompile
int runCompact(PrintWriter pw) {
- String processName = getNextArgRequired();
- String uid = getNextArgRequired();
- String op = getNextArgRequired();
ProcessRecord app;
- synchronized (mInternal.mProcLock) {
- app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
- }
- pw.println("Process record found pid: " + app.mPid);
- if (op.equals("full")) {
- pw.println("Executing full compaction for " + app.mPid);
+ String op = getNextArgRequired();
+ boolean isFullCompact = op.equals("full");
+ boolean isSomeCompact = op.equals("some");
+ if (isFullCompact || isSomeCompact) {
+ String processName = getNextArgRequired();
+ String uid = getNextArgRequired();
synchronized (mInternal.mProcLock) {
- mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppFull(app, true);
+ app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
}
- pw.println("Finished full compaction for " + app.mPid);
- } else if (op.equals("some")) {
- pw.println("Executing some compaction for " + app.mPid);
+ pw.println("Process record found pid: " + app.mPid);
+ if (isFullCompact) {
+ pw.println("Executing full compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+ CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.APP, true);
+ }
+ pw.println("Finished full compaction for " + app.mPid);
+ } else if (isSomeCompact) {
+ pw.println("Executing some compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+ CachedAppOptimizer.CompactProfile.SOME,
+ CachedAppOptimizer.CompactSource.APP, true);
+ }
+ pw.println("Finished some compaction for " + app.mPid);
+ }
+ } else if (op.equals("system")) {
+ pw.println("Executing system compaction");
synchronized (mInternal.mProcLock) {
- mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppSome(app, true);
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
}
- pw.println("Finished some compaction for " + app.mPid);
+ pw.println("Finished system compaction");
} else {
getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
return -1;
@@ -3570,10 +3584,11 @@
pw.println(" --allow-background-activity-starts: The receiver may start activities");
pw.println(" even if in the background.");
pw.println(" --async: Send without waiting for the completion of the receiver.");
- pw.println(" compact <process_name> <Package UID> [some|full]");
+ pw.println(" compact [some|full|system] <process_name> <Package UID>");
pw.println(" Force process compaction.");
pw.println(" some: execute file compaction.");
pw.println(" full: execute anon + file compaction.");
+ pw.println(" system: system compaction.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c88d82c..45265ac 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,7 +25,6 @@
import android.app.ApplicationExitInfo;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.Debug;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManagerInternal;
@@ -40,20 +39,21 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
-
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -99,7 +99,7 @@
private static final int COMPACT_ACTION_NONE = 0;
private static final int COMPACT_ACTION_FILE = 1;
private static final int COMPACT_ACTION_ANON = 2;
- private static final int COMPACT_ACTION_FULL = 3;
+ private static final int COMPACT_ACTION_ALL = 3;
private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
@@ -112,7 +112,7 @@
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
@VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@@ -147,14 +147,33 @@
@VisibleForTesting
interface ProcessDependencies {
long[] getRss(int pid);
- void performCompaction(String action, int pid) throws IOException;
+ void performCompaction(CompactAction action, int pid) throws IOException;
+ }
+
+ // This indicates the compaction we want to perform
+ public enum CompactProfile {
+ SOME, // File compaction
+ FULL // File+anon compaction
+ }
+
+ // Low level actions that can be performed for compaction
+ // currently determined by the compaction profile
+ public enum CompactAction {
+ NONE, // No compaction
+ FILE, // File+anon compaction
+ ANON,
+ ALL
+ }
+
+ // This indicates the process OOM memory state that initiated the compaction request
+ public enum CompactSource { APP, PERSISTENT, BFGS }
+
+ public enum CancelCompactReason {
+ SCREEN_ON, // screen was turned on which cancels all compactions.
+ OOM_IMPROVEMENT // process moved out of cached state and into a more perceptible state.
}
// Handler constants.
- static final int COMPACT_PROCESS_SOME = 1;
- static final int COMPACT_PROCESS_FULL = 2;
- static final int COMPACT_PROCESS_PERSISTENT = 3;
- static final int COMPACT_PROCESS_BFGS = 4;
static final int COMPACT_PROCESS_MSG = 1;
static final int COMPACT_SYSTEM_MSG = 2;
static final int SET_FROZEN_PROCESS_MSG = 3;
@@ -165,6 +184,12 @@
// on swap resources as file.
static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2;
+ // Size of history for the last 20 compactions for any process
+ static final int LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE = 20;
+
+ // Amount of processes supported to record for their last compaction.
+ static final int LAST_COMPACTION_FOR_PROCESS_STATS_SIZE = 256;
+
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
@@ -273,15 +298,16 @@
private final SettingsContentObserver mSettingsObserver;
- private final Object mPhenotypeFlagLock = new Object();
+ @VisibleForTesting
+ final Object mPhenotypeFlagLock = new Object();
// Configured by phenotype. Updates from the server take effect immediately.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile String mCompactActionSome =
- compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
+ @VisibleForTesting
+ volatile CompactAction mCompactActionSome = compactActionIntToAction(DEFAULT_COMPACT_ACTION_1);
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile String mCompactActionFull =
- compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
+ @VisibleForTesting
+ volatile CompactAction mCompactActionFull = compactActionIntToAction(DEFAULT_COMPACT_ACTION_2);
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
@GuardedBy("mPhenotypeFlagLock")
@@ -333,31 +359,139 @@
// facilitate removal of the oldest entry.
@VisibleForTesting
@GuardedBy("mProcLock")
- LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
- new LinkedHashMap<Integer, LastCompactionStats>() {
+ LinkedHashMap<Integer, SingleCompactionStats> mLastCompactionStats =
+ new LinkedHashMap<Integer, SingleCompactionStats>() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > 100;
+ return size() > LAST_COMPACTION_FOR_PROCESS_STATS_SIZE;
}
- };
+ };
- // Compaction Stats
- private int mSomeCompactionCount;
- private int mFullCompactionCount;
- private int mPersistentCompactionCount;
- private int mBfgsCompactionCount;
- private long mSomeCompactRequest;
- private long mFullCompactRequest;
- private long mPersistentCompactRequest;
- private long mBfgsCompactRequest;
- private long mProcCompactionsRequested;
- private long mProcCompactionsPerformed;
- private long mProcCompactionsNoPidThrottled;
- private long mProcCompactionsOomAdjThrottled;
- private long mProcCompactionsTimeThrottled;
- private long mProcCompactionsRSSThrottled;
- private long mProcCompactionsMiscThrottled;
+ LinkedList<SingleCompactionStats> mCompactionStatsHistory =
+ new LinkedList<SingleCompactionStats>() {
+ @Override
+ public boolean add(SingleCompactionStats e) {
+ if (size() >= LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE) {
+ this.remove();
+ }
+ return super.add(e);
+ }
+ };
+
+ class AggregatedCompactionStats {
+ // Throttling stats
+ public long mFullCompactRequested;
+ public long mSomeCompactRequested;
+ public long mFullCompactPerformed;
+ public long mSomeCompactPerformed;
+ public long mProcCompactionsNoPidThrottled;
+ public long mProcCompactionsOomAdjThrottled;
+ public long mProcCompactionsTimeThrottled;
+ public long mProcCompactionsRSSThrottled;
+ public long mProcCompactionsMiscThrottled;
+
+ // Memory stats
+ public long mTotalDeltaAnonRssKBs;
+ public long mTotalZramConsumedKBs;
+ public long mTotalAnonMemFreedKBs;
+ public long mSumOrigAnonRss;
+ public double mMaxCompactEfficiency;
+
+ // Cpu time
+ public long mTotalCpuTimeMillis;
+
+ public long getThrottledSome() { return mSomeCompactRequested - mSomeCompactPerformed; }
+
+ public long getThrottledFull() { return mFullCompactRequested - mFullCompactPerformed; }
+
+ public void addMemStats(long anonRssSaved, long zramConsumed, long memFreed,
+ long origAnonRss, long totalCpuTimeMillis) {
+ final double compactEfficiency = memFreed / (double) origAnonRss;
+ if (compactEfficiency > mMaxCompactEfficiency) {
+ mMaxCompactEfficiency = compactEfficiency;
+ }
+ mTotalDeltaAnonRssKBs += anonRssSaved;
+ mTotalZramConsumedKBs += zramConsumed;
+ mTotalAnonMemFreedKBs += memFreed;
+ mSumOrigAnonRss += origAnonRss;
+ mTotalCpuTimeMillis += totalCpuTimeMillis;
+ }
+
+ public void dump(PrintWriter pw) {
+ long totalCompactRequested = mSomeCompactRequested + mFullCompactRequested;
+ long totalCompactPerformed = mSomeCompactPerformed + mFullCompactPerformed;
+ pw.println(" Performed / Requested:");
+ pw.println(" Some: (" + mSomeCompactPerformed + "/" + mSomeCompactRequested + ")");
+ pw.println(" Full: (" + mFullCompactPerformed + "/" + mFullCompactRequested + ")");
+
+ long throttledSome = getThrottledSome();
+ long throttledFull = getThrottledFull();
+
+ if (throttledSome > 0 || throttledFull > 0) {
+ pw.println(" Throttled:");
+ pw.println(" Some: " + throttledSome + " Full: " + throttledFull);
+ pw.println(" Throttled by Type:");
+ final long compactionsThrottled = totalCompactRequested - totalCompactPerformed;
+ // Any throttle that was not part of the previous categories
+ final long unaccountedThrottled = compactionsThrottled
+ - mProcCompactionsNoPidThrottled - mProcCompactionsOomAdjThrottled
+ - mProcCompactionsTimeThrottled - mProcCompactionsRSSThrottled
+ - mProcCompactionsMiscThrottled;
+ pw.println(" NoPid: " + mProcCompactionsNoPidThrottled
+ + " OomAdj: " + mProcCompactionsOomAdjThrottled + " Time: "
+ + mProcCompactionsTimeThrottled + " RSS: " + mProcCompactionsRSSThrottled
+ + " Misc: " + mProcCompactionsMiscThrottled
+ + " Unaccounted: " + unaccountedThrottled);
+ final double compactThrottlePercentage =
+ (compactionsThrottled / (double) totalCompactRequested) * 100.0;
+ pw.println(" Throttle Percentage: " + compactThrottlePercentage);
+ }
+
+ if (mFullCompactPerformed > 0) {
+ pw.println(" -----Memory Stats----");
+ pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
+ pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+ pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+ // This tells us how much anon memory we were able to free thanks to running
+ // compaction
+ pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): "
+ + (mTotalAnonMemFreedKBs / (double) mSumOrigAnonRss));
+ pw.println(" Max Compaction Efficiency: " + mMaxCompactEfficiency);
+ // This tells us how effective is the compression algorithm in physical memory
+ pw.println(" Avg Compression Ratio (1 - ZRAM Consumed/DeltaAnonRSS): "
+ + (1.0 - mTotalZramConsumedKBs / (double) mTotalDeltaAnonRssKBs));
+ long avgKBsPerProcCompact = mFullCompactPerformed > 0
+ ? (mTotalAnonMemFreedKBs / mFullCompactPerformed)
+ : 0;
+ pw.println(" Avg Anon Mem Freed/Compaction (KB) : " + avgKBsPerProcCompact);
+ double compactionCost =
+ mTotalCpuTimeMillis / (mTotalAnonMemFreedKBs / 1024.0); // ms/MB
+ pw.println(" Compaction Cost (ms/MB): " + compactionCost);
+ }
+ }
+ }
+
+ class AggregatedProcessCompactionStats extends AggregatedCompactionStats {
+ public final String processName;
+
+ AggregatedProcessCompactionStats(String processName) { this.processName = processName; }
+ }
+
+ class AggregatedSourceCompactionStats extends AggregatedCompactionStats {
+ public final CompactSource sourceType;
+
+ AggregatedSourceCompactionStats(CompactSource sourceType) { this.sourceType = sourceType; }
+ }
+
+ private final LinkedHashMap<String, AggregatedProcessCompactionStats> mPerProcessCompactStats =
+ new LinkedHashMap<>(256);
+ private final EnumMap<CompactSource, AggregatedSourceCompactionStats> mPerSourceCompactStats =
+ new EnumMap<>(CompactSource.class);
+ private long mTotalCompactionDowngrades;
private long mSystemCompactionsPerformed;
+ private long mSystemTotalMemFreed;
+ private EnumMap<CancelCompactReason, Integer> mTotalCompactionsCancelled =
+ new EnumMap<>(CancelCompactReason.class);
private final ProcessDependencies mProcessDependencies;
private final ProcLocksReader mProcLocksReader;
@@ -450,34 +584,61 @@
pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "="
+ Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
- pw.println(" Requested: " + mSomeCompactRequest + " some, " + mFullCompactRequest
- + " full, " + mPersistentCompactRequest + " persistent, "
- + mBfgsCompactRequest + " BFGS compactions.");
- pw.println(" Performed: " + mSomeCompactionCount + " some, " + mFullCompactionCount
- + " full, " + mPersistentCompactionCount + " persistent, "
- + mBfgsCompactionCount + " BFGS compactions.");
- pw.println(" Process Compactions Requested: " + mProcCompactionsRequested);
- pw.println(" Process Compactions Performed: " + mProcCompactionsPerformed);
- long compactionsThrottled = mProcCompactionsRequested - mProcCompactionsPerformed;
- pw.println(" Process Compactions Throttled: " + compactionsThrottled);
- double compactThrottlePercentage =
- (compactionsThrottled / (double) mProcCompactionsRequested) * 100.0;
- pw.println(" Process Compactions Throttle Percentage: " + compactThrottlePercentage);
- pw.println(" NoPid Throttled: " + mProcCompactionsNoPidThrottled);
- pw.println(" OomAdj Throttled: " + mProcCompactionsOomAdjThrottled);
- pw.println(" Time Throttled: " + mProcCompactionsTimeThrottled);
- pw.println(" RSS Throttled: " + mProcCompactionsRSSThrottled);
- pw.println(" Misc Throttled: " + mProcCompactionsMiscThrottled);
- long unaccountedThrottled = compactionsThrottled - mProcCompactionsNoPidThrottled
- - mProcCompactionsOomAdjThrottled - mProcCompactionsTimeThrottled
- - mProcCompactionsRSSThrottled - mProcCompactionsMiscThrottled;
- // Any throttle that was not part of the previous categories
- pw.println(" Unaccounted Throttled: " + unaccountedThrottled);
+ pw.println(" Per-Process Compaction Stats");
+ long totalCompactPerformedSome = 0;
+ long totalCompactPerformedFull = 0;
+ for (AggregatedProcessCompactionStats stats : mPerProcessCompactStats.values()) {
+ pw.println("-----" + stats.processName + "-----");
+ totalCompactPerformedSome += stats.mSomeCompactPerformed;
+ totalCompactPerformedFull += stats.mFullCompactPerformed;
+ stats.dump(pw);
+ pw.println();
+ }
+ pw.println();
+ pw.println(" Per-Source Compaction Stats");
+ for (AggregatedSourceCompactionStats stats : mPerSourceCompactStats.values()) {
+ pw.println("-----" + stats.sourceType + "-----");
+ stats.dump(pw);
+ pw.println();
+ }
+ pw.println();
- pw.println(" System Compactions Performed: " + mSystemCompactionsPerformed);
+ pw.println("Total Compactions Performed by profile: " + totalCompactPerformedSome
+ + " some, " + totalCompactPerformedFull + " full");
+ pw.println("Total compactions downgraded: " + mTotalCompactionDowngrades);
+ pw.println("Total compactions cancelled by reason: ");
+ for (CancelCompactReason reason : mTotalCompactionsCancelled.keySet()) {
+ pw.println(" " + reason + ": " + mTotalCompactionsCancelled.get(reason));
+ }
+ pw.println();
+ pw.println(" System Compaction Memory Stats");
+ pw.println(" Compactions Performed: " + mSystemCompactionsPerformed);
+ pw.println(" Total Memory Freed (KB): " + mSystemTotalMemFreed);
+ double avgKBsPerSystemCompact = mSystemCompactionsPerformed > 0
+ ? mSystemTotalMemFreed / mSystemCompactionsPerformed
+ : 0;
+ pw.println(" Avg Mem Freed per Compact (KB): " + avgKBsPerSystemCompact);
+ pw.println();
pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size()
+ " processes.");
+ pw.println("Last Compaction per process stats:");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+ + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ for (Map.Entry<Integer, SingleCompactionStats> entry :
+ mLastCompactionStats.entrySet()) {
+ SingleCompactionStats stats = entry.getValue();
+ stats.dump(pw);
+ }
+ pw.println();
+ pw.println("Last 20 Compactions Stats:");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+ + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ for (SingleCompactionStats stats : mCompactionStatsHistory) {
+ stats.dump(pw);
+ }
+ pw.println();
+
pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
@@ -500,25 +661,9 @@
}
}
}
- if (DEBUG_COMPACTION) {
- for (Map.Entry<Integer, LastCompactionStats> entry
- : mLastCompactionStats.entrySet()) {
- int pid = entry.getKey();
- LastCompactionStats stats = entry.getValue();
- pw.println(" " + pid + ": "
- + Arrays.toString(stats.getRssAfterCompaction()));
- }
- }
}
}
- @GuardedBy("mProcLock")
- void compactAppSome(ProcessRecord app, boolean force) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
- ++mSomeCompactRequest;
- compactApp(app, force, "some");
- }
-
// This method returns true only if requirements are met. Note, that requirements are different
// from throttles applied at the time a compaction is trying to be executed in the sense that
// these are not subject to change dependent on time or memory as throttles usually do.
@@ -546,46 +691,38 @@
}
@GuardedBy("mProcLock")
- void compactAppFull(ProcessRecord app, boolean force) {
- boolean oomAdjEnteredCached = (app.mState.getSetAdj() < mCompactThrottleMinOomAdj
- || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
- && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
- && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj;
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- " compactAppFull requested for " + app.processName + " force: " + force
- + " oomAdjEnteredCached: " + oomAdjEnteredCached);
+ boolean compactApp(
+ ProcessRecord app, CompactProfile compactProfile, CompactSource source, boolean force) {
+ app.mOptRecord.setReqCompactSource(source);
+ app.mOptRecord.setReqCompactProfile(compactProfile);
+ AggregatedSourceCompactionStats perSourceStats = getPerSourceAggregatedCompactStat(source);
+ AggregatedCompactionStats perProcStats =
+ getPerProcessAggregatedCompactStat(app.processName);
+ switch (compactProfile) {
+ case SOME:
+ ++perProcStats.mSomeCompactRequested;
+ ++perSourceStats.mSomeCompactRequested;
+ break;
+ case FULL:
+ ++perProcStats.mFullCompactRequested;
+ ++perSourceStats.mFullCompactRequested;
+ break;
+ default:
+ Slog.e(TAG_AM,
+ "Unimplemented compaction type, consider adding it.");
+ return false;
}
- ++mFullCompactRequest;
- // Apply OOM adj score throttle for Full App Compaction.
- if (force || oomAdjEnteredCached) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
- compactApp(app, force, "Full");
- } else {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
- + " oom adj score changed from " + app.mState.getSetAdj()
- + " to " + app.mState.getCurAdj());
- }
- }
- }
- @GuardedBy("mProcLock")
- void compactAppPersistent(ProcessRecord app) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
- ++mPersistentCompactRequest;
- compactApp(app, false, "Persistent");
- }
-
- @GuardedBy("mProcLock")
- boolean compactApp(ProcessRecord app, boolean force, String compactRequestType) {
- if (!app.mOptRecord.hasPendingCompact() && meetsCompactionRequirements(app)) {
+ if (!app.mOptRecord.hasPendingCompact() && (meetsCompactionRequirements(app) || force)) {
final String processName = (app.processName != null ? app.processName : "");
if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "compactApp " + compactRequestType + " " + processName);
+ Slog.d(TAG_AM,
+ "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+ + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
}
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
- "compactApp " + compactRequestType + " " + processName);
+ "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+ + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
app.mOptRecord.setHasPendingCompact(true);
app.mOptRecord.setForceCompact(force);
mPendingCompactionProcesses.add(app);
@@ -596,14 +733,53 @@
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
- " compactApp Skipped for " + app.processName
- + " pendingCompact= " + app.mOptRecord.hasPendingCompact()
- + " meetsCompactionRequirements=" + meetsCompactionRequirements(app)
- + ". Requested compact: " + app.mOptRecord.getReqCompactAction());
+ " compactApp Skipped for " + app.processName + " pendingCompact= "
+ + app.mOptRecord.hasPendingCompact() + " meetsCompactionRequirements="
+ + meetsCompactionRequirements(app) + ". Requested compact profile: "
+ + app.mOptRecord.getReqCompactProfile().name() + ". Compact source "
+ + app.mOptRecord.getReqCompactSource().name());
}
return false;
}
+ private CompactAction resolveCompactActionForProfile(CompactProfile profile) {
+ CompactAction action;
+ switch (profile) {
+ case SOME:
+ action = CompactAction.FILE;
+ break;
+ case FULL:
+ action = CompactAction.ALL;
+ break;
+ default:
+ action = CompactAction.NONE;
+ }
+ return action;
+ }
+
+ private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat(
+ String processName) {
+ if (processName == null) {
+ processName = "";
+ }
+ AggregatedProcessCompactionStats stats = mPerProcessCompactStats.get(processName);
+ if (stats == null) {
+ stats = new AggregatedProcessCompactionStats(processName);
+ mPerProcessCompactStats.put(processName, stats);
+ }
+ return stats;
+ }
+
+ private AggregatedSourceCompactionStats getPerSourceAggregatedCompactStat(
+ CompactSource source) {
+ AggregatedSourceCompactionStats stats = mPerSourceCompactStats.get(source);
+ if (stats == null) {
+ stats = new AggregatedSourceCompactionStats(source);
+ mPerSourceCompactStats.put(source, stats);
+ }
+ return stats;
+ }
+
@GuardedBy("mProcLock")
boolean shouldCompactPersistent(ProcessRecord app, long now) {
return (app.mOptRecord.getLastCompactTime() == 0
@@ -611,13 +787,6 @@
}
@GuardedBy("mProcLock")
- void compactAppBfgs(ProcessRecord app) {
- ++mBfgsCompactRequest;
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
- compactApp(app, false, " Bfgs");
- }
-
- @GuardedBy("mProcLock")
boolean shouldCompactBFGS(ProcessRecord app, long now) {
return (app.mOptRecord.getLastCompactTime() == 0
|| (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
@@ -648,11 +817,27 @@
static private native void cancelCompaction();
/**
+ * Returns the cpu time for the current thread
+ */
+ static private native long threadCpuTimeNs();
+
+ /**
* Retrieves the free swap percentage.
*/
static private native double getFreeSwapPercent();
/**
+ * Retrieves the total used physical ZRAM
+ */
+ static private native long getUsedZramMemory();
+
+ /**
+ * Amount of memory that has been made free due to compaction.
+ * It represents uncompressed memory size - compressed memory size.
+ */
+ static private native long getMemoryFreedCompaction();
+
+ /**
* Reads the flag value from DeviceConfig to determine whether app compaction
* should be enabled, and starts the freeze/compaction thread if needed.
*/
@@ -851,8 +1036,8 @@
int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
- mCompactActionSome = compactActionIntToString(compactAction1);
- mCompactActionFull = compactActionIntToString(compactAction2);
+ mCompactActionSome = compactActionIntToAction(compactAction1);
+ mCompactActionFull = compactActionIntToAction(compactAction2);
}
@GuardedBy("mPhenotypeFlagLock")
@@ -1019,13 +1204,12 @@
return true;
}
- @VisibleForTesting
- static String compactActionIntToString(int action) {
- if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
- return "";
+ static CompactAction compactActionIntToAction(int action) {
+ if (action < 0 || action >= CompactAction.values().length) {
+ return CompactAction.NONE;
}
- return COMPACT_ACTION_STRING[action];
+ return CompactAction.values()[action];
}
// This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
@@ -1202,16 +1386,17 @@
if(wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// Remove any pending compaction we may have scheduled to happen while screen was off
Slog.e(TAG_AM, "Cancel pending or running compactions as system is awake");
- cancelAllCompactions();
+ cancelAllCompactions(CancelCompactReason.SCREEN_ON);
}
}
- void cancelAllCompactions() {
+ void cancelAllCompactions(CancelCompactReason reason) {
synchronized (mProcLock) {
int size = mPendingCompactionProcesses.size();
ProcessRecord record;
for (int i=0; i < size; ++i) {
record = mPendingCompactionProcesses.get(i);
+ cancelCompactionForProcess(record, reason);
// The process record is kept alive after compactions are cleared,
// so make sure to reset the compaction state to avoid skipping any future
// compactions due to a stale value here.
@@ -1222,53 +1407,64 @@
cancelCompaction();
}
+ @GuardedBy("mProcLock")
+ void cancelCompactionForProcess(ProcessRecord app, CancelCompactReason cancelReason) {
+ boolean cancelled = false;
+ if (!mPendingCompactionProcesses.isEmpty() && mPendingCompactionProcesses.contains(app)) {
+ app.mOptRecord.setHasPendingCompact(false);
+ mPendingCompactionProcesses.remove(app);
+ cancelled = true;
+ }
+ if (DefaultProcessDependencies.mPidCompacting == app.mPid) {
+ cancelCompaction();
+ cancelled = true;
+ }
+ if (cancelled) {
+ if (mTotalCompactionsCancelled.containsKey(cancelReason)) {
+ int count = mTotalCompactionsCancelled.get(cancelReason);
+ mTotalCompactionsCancelled.put(cancelReason, count + 1);
+ } else {
+ mTotalCompactionsCancelled.put(cancelReason, 1);
+ }
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Cancelled pending or running compactions for process: " +
+ app.processName != null ? app.processName : "" +
+ " reason: " + cancelReason.name());
+ }
+ }
+ }
+
@GuardedBy({"mService", "mProcLock"})
void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
// Cancel any currently executing compactions
// if the process moved out of cached state
- if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
- && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
- cancelCompaction();
+ if (newAdj < oldAdj && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ cancelCompactionForProcess(app, CancelCompactReason.OOM_IMPROVEMENT);
}
if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
&& (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
- compactAppSome(app, false);
+ compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
} else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
&& newAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
// Perform a major compaction when any app enters cached
- compactAppFull(app, false);
+ compactApp(app, CompactProfile.FULL, CompactSource.APP, false);
}
}
/**
- * This method resolves which compaction method we should use for the proposed compaction.
+ * Applies a compaction downgrade when swap is low.
*/
- int resolveCompactionAction(int pendingAction) {
- int resolvedAction;
-
- switch (pendingAction) {
- case COMPACT_PROCESS_SOME:
- resolvedAction = COMPACT_ACTION_FILE;
- break;
- // For the time being, treat these as equivalent.
- case COMPACT_PROCESS_FULL:
- case COMPACT_PROCESS_PERSISTENT:
- case COMPACT_PROCESS_BFGS:
- resolvedAction = COMPACT_ACTION_FULL;
- break;
- default:
- resolvedAction = COMPACT_ACTION_NONE;
- break;
- }
-
+ CompactProfile downgradeCompactionIfRequired(CompactProfile profile) {
// Downgrade compaction under swap memory pressure
- if (resolvedAction == COMPACT_ACTION_FULL) {
+ if (profile == CompactProfile.FULL) {
double swapFreePercent = getFreeSwapPercent();
if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
- resolvedAction = COMPACT_ACTION_FILE;
+ profile = CompactProfile.SOME;
+ ++mTotalCompactionDowngrades;
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
"Downgraded compaction to file only due to low swap."
@@ -1277,20 +1473,69 @@
}
}
- return resolvedAction;
+ return profile;
}
@VisibleForTesting
- static final class LastCompactionStats {
+ static final class SingleCompactionStats {
+ private static final float STATSD_SAMPLE_RATE = 0.1f;
+ private static final Random mRandom = new Random();
private final long[] mRssAfterCompaction;
+ public CompactSource mSourceType;
+ public String mProcessName;
+ public final int mUid;
+ public long mDeltaAnonRssKBs;
+ public long mZramConsumedKBs;
+ public long mAnonMemFreedKBs;
+ public float mCpuTimeMillis;
+ public long mOrigAnonRss;
+ public int mProcState;
+ public int mOomAdj;
+ public @OomAdjuster.OomAdjReason int mOomAdjReason;
- LastCompactionStats(long[] rss) {
+ SingleCompactionStats(long[] rss, CompactSource source, String processName,
+ long deltaAnonRss, long zramConsumed, long anonMemFreed, long origAnonRss,
+ long cpuTimeMillis, int procState, int oomAdj,
+ @OomAdjuster.OomAdjReason int oomAdjReason, int uid) {
mRssAfterCompaction = rss;
+ mSourceType = source;
+ mProcessName = processName;
+ mUid = uid;
+ mDeltaAnonRssKBs = deltaAnonRss;
+ mZramConsumedKBs = zramConsumed;
+ mAnonMemFreedKBs = anonMemFreed;
+ mCpuTimeMillis = cpuTimeMillis;
+ mOrigAnonRss = origAnonRss;
+ mProcState = procState;
+ mOomAdj = oomAdj;
+ mOomAdjReason = oomAdjReason;
+ }
+
+ double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
+
+ double getCompactCost() {
+ // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
+ return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
}
long[] getRssAfterCompaction() {
return mRssAfterCompaction;
}
+
+ void dump(PrintWriter pw) {
+ pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
+ + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+ + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
+ + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
+ }
+
+ void sendStat() {
+ if (mRandom.nextFloat() < STATSD_SAMPLE_RATE) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED_V2, mUid, mProcState,
+ mOomAdj, mDeltaAnonRssKBs, mZramConsumedKBs, mCpuTimeMillis, mOrigAnonRss,
+ mOomAdjReason);
+ }
+ }
}
private final class MemCompactionHandler extends Handler {
@@ -1298,13 +1543,12 @@
super(mCachedAppOptimizerThread.getLooper());
}
- private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) {
+ private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc) {
final String name = proc.processName;
// don't compact if the process has returned to perceptible
// and this is only a cached/home/prev compaction
- if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL)
- && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+ if (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
"Skipping compaction as process " + name + " is "
@@ -1316,51 +1560,52 @@
return false;
}
- private boolean shouldTimeThrottleCompaction(
- ProcessRecord proc, long start, int pendingAction) {
+ private boolean shouldTimeThrottleCompaction(ProcessRecord proc, long start,
+ CompactProfile pendingProfile, CompactSource source) {
final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
final String name = proc.processName;
- int lastCompactAction = opt.getLastCompactAction();
+ CompactProfile lastCompactProfile = opt.getLastCompactProfile();
long lastCompactTime = opt.getLastCompactTime();
// basic throttling
- // use the Phenotype flag knobs to determine whether current/prevous
- // compaction combo should be throtted or not
-
+ // use the Phenotype flag knobs to determine whether current/previous
+ // compaction combo should be throttled or not.
// Note that we explicitly don't take mPhenotypeFlagLock here as the flags
// should very seldom change, and taking the risk of using the wrong action is
// preferable to taking the lock for every single compaction action.
if (lastCompactTime != 0) {
- if (pendingAction == COMPACT_PROCESS_SOME) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleSomeSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime < mCompactThrottleSomeFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping some compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleSomeSome
- + "/" + mCompactThrottleSomeFull
- + " last=" + (start - lastCompactTime) + "ms ago");
+ if (source == CompactSource.APP) {
+ if (pendingProfile == CompactProfile.SOME) {
+ if ((lastCompactProfile == CompactProfile.SOME
+ && (start - lastCompactTime < mCompactThrottleSomeSome))
+ || (lastCompactProfile == CompactProfile.FULL
+ && (start - lastCompactTime < mCompactThrottleSomeFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping some compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleSomeSome
+ + "/" + mCompactThrottleSomeFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
}
- return true;
- }
- } else if (pendingAction == COMPACT_PROCESS_FULL) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleFullSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime < mCompactThrottleFullFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping full compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleFullSome
- + "/" + mCompactThrottleFullFull
- + " last=" + (start - lastCompactTime) + "ms ago");
+ } else if (pendingProfile == CompactProfile.FULL) {
+ if ((lastCompactProfile == CompactProfile.SOME
+ && (start - lastCompactTime < mCompactThrottleFullSome))
+ || (lastCompactProfile == CompactProfile.FULL
+ && (start - lastCompactTime < mCompactThrottleFullFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleFullSome
+ + "/" + mCompactThrottleFullFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
}
- return true;
}
- } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
+ } else if (source == CompactSource.PERSISTENT) {
if (start - lastCompactTime < mCompactThrottlePersistent) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1370,7 +1615,7 @@
}
return true;
}
- } else if (pendingAction == COMPACT_PROCESS_BFGS) {
+ } else if (source == CompactSource.BFGS) {
if (start - lastCompactTime < mCompactThrottleBFGS) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1386,11 +1631,10 @@
return false;
}
- private boolean shouldThrottleMiscCompaction(
- ProcessRecord proc, int procState, int action) {
- final String name = proc.processName;
+ private boolean shouldThrottleMiscCompaction(ProcessRecord proc, int procState) {
if (mProcStateThrottle.contains(procState)) {
if (DEBUG_COMPACTION) {
+ final String name = proc.processName;
Slog.d(TAG_AM,
"Skipping full compaction for process " + name + "; proc state is "
+ procState);
@@ -1398,21 +1642,13 @@
return true;
}
- if (COMPACT_ACTION_NONE == action) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping compaction for process " + name + "since action is None");
- }
- return true;
- }
-
return false;
}
private boolean shouldRssThrottleCompaction(
- int action, int pid, String name, long[] rssBefore) {
+ CompactProfile profile, int pid, String name, long[] rssBefore) {
long anonRssBefore = rssBefore[RSS_ANON_INDEX];
- LastCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
+ SingleCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0
&& rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) {
@@ -1424,7 +1660,7 @@
return true;
}
- if (action == COMPACT_ACTION_FULL || action == COMPACT_ACTION_ANON) {
+ if (profile == CompactProfile.FULL) {
if (mFullAnonRssThrottleKb > 0L && anonRssBefore < mFullAnonRssThrottleKb) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1461,13 +1697,16 @@
ProcessRecord proc;
final ProcessCachedOptimizerRecord opt;
int pid;
- String action;
+ CompactAction resolvedAction;
final String name;
- int requestedAction, lastCompactAction;
+ CompactProfile lastCompactProfile;
long lastCompactTime;
- int lastOomAdj = msg.arg1;
+ int newOomAdj = msg.arg1;
int procState = msg.arg2;
boolean forceCompaction;
+ CompactSource compactSource;
+ CompactProfile requestedProfile;
+ int oomAdjReason;
synchronized (mProcLock) {
if (mPendingCompactionProcesses.isEmpty()) {
if (DEBUG_COMPACTION) {
@@ -1479,42 +1718,53 @@
opt = proc.mOptRecord;
forceCompaction = opt.isForceCompact();
opt.setForceCompact(false); // since this is a one-shot operation
-
- requestedAction = opt.getReqCompactAction();
pid = proc.getPid();
name = proc.processName;
opt.setHasPendingCompact(false);
- lastCompactAction = opt.getLastCompactAction();
+ compactSource = opt.getReqCompactSource();
+ requestedProfile = opt.getReqCompactProfile();
+ lastCompactProfile = opt.getLastCompactProfile();
lastCompactTime = opt.getLastCompactTime();
+ oomAdjReason = opt.getLastOomAdjChangeReason();
}
- ++mProcCompactionsRequested;
+ AggregatedSourceCompactionStats perSourceStats =
+ getPerSourceAggregatedCompactStat(opt.getReqCompactSource());
+ AggregatedProcessCompactionStats perProcessStats =
+ getPerProcessAggregatedCompactStat(name);
+
long[] rssBefore;
if (pid == 0) {
// not a real process, either one being launched or one being killed
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM, "Compaction failed, pid is 0");
}
- ++mProcCompactionsNoPidThrottled;
+ ++perSourceStats.mProcCompactionsNoPidThrottled;
+ ++perProcessStats.mProcCompactionsNoPidThrottled;
return;
}
if (!forceCompaction) {
- if (shouldOomAdjThrottleCompaction(proc, requestedAction)) {
- ++mProcCompactionsOomAdjThrottled;
+ if (shouldOomAdjThrottleCompaction(proc)) {
+ ++perProcessStats.mProcCompactionsOomAdjThrottled;
+ ++perSourceStats.mProcCompactionsOomAdjThrottled;
return;
}
- if (shouldTimeThrottleCompaction(proc, start, requestedAction)) {
- ++mProcCompactionsTimeThrottled;
+ if (shouldTimeThrottleCompaction(
+ proc, start, requestedProfile, compactSource)) {
+ ++perProcessStats.mProcCompactionsTimeThrottled;
+ ++perSourceStats.mProcCompactionsTimeThrottled;
return;
}
- if (shouldThrottleMiscCompaction(proc, procState, requestedAction)) {
- ++mProcCompactionsMiscThrottled;
+ if (shouldThrottleMiscCompaction(proc, procState)) {
+ ++perProcessStats.mProcCompactionsMiscThrottled;
+ ++perSourceStats.mProcCompactionsMiscThrottled;
return;
}
rssBefore = mProcessDependencies.getRss(pid);
- if (shouldRssThrottleCompaction(requestedAction, pid, name, rssBefore)) {
- ++mProcCompactionsRSSThrottled;
+ if (shouldRssThrottleCompaction(requestedProfile, pid, name, rssBefore)) {
+ ++perProcessStats.mProcCompactionsRSSThrottled;
+ ++perSourceStats.mProcCompactionsRSSThrottled;
return;
}
} else {
@@ -1524,71 +1774,78 @@
}
}
- // Now we've passed through all the throttles and are going to compact, update
- // bookkeeping.
- switch (requestedAction) {
- case COMPACT_PROCESS_SOME:
- mSomeCompactionCount++;
- break;
- case COMPACT_PROCESS_FULL:
- mFullCompactionCount++;
- break;
- case COMPACT_PROCESS_PERSISTENT:
- mPersistentCompactionCount++;
- break;
- case COMPACT_PROCESS_BFGS:
- mBfgsCompactionCount++;
- break;
- default:
- break;
- }
-
- int resolvedAction = resolveCompactionAction(requestedAction);
- action = compactActionIntToString(resolvedAction);
+ CompactProfile resolvedProfile =
+ downgradeCompactionIfRequired(requestedProfile);
+ resolvedAction = resolveCompactActionForProfile(resolvedProfile);
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "Compact " + action + ": " + name);
- ++mProcCompactionsPerformed;
- long zramFreeKbBefore = Debug.getZramFreeKb();
- mProcessDependencies.performCompaction(action, pid);
+ "Compact " + resolvedAction.name() + ": " + name
+ + " lastOomAdjReason: " + oomAdjReason);
+ long zramUsedKbBefore = getUsedZramMemory();
+ long startCpuTime = threadCpuTimeNs();
+ mProcessDependencies.performCompaction(resolvedAction, pid);
+ long endCpuTime = threadCpuTimeNs();
long[] rssAfter = mProcessDependencies.getRss(pid);
long end = SystemClock.uptimeMillis();
long time = end - start;
- long zramFreeKbAfter = Debug.getZramFreeKb();
+ long deltaCpuTimeNanos = endCpuTime - startCpuTime;
+ long zramUsedKbAfter = getUsedZramMemory();
long deltaTotalRss = rssAfter[RSS_TOTAL_INDEX] - rssBefore[RSS_TOTAL_INDEX];
long deltaFileRss = rssAfter[RSS_FILE_INDEX] - rssBefore[RSS_FILE_INDEX];
long deltaAnonRss = rssAfter[RSS_ANON_INDEX] - rssBefore[RSS_ANON_INDEX];
long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX];
- EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
- rssBefore[RSS_TOTAL_INDEX], rssBefore[RSS_FILE_INDEX],
- rssBefore[RSS_ANON_INDEX], rssBefore[RSS_SWAP_INDEX], deltaTotalRss,
- deltaFileRss, deltaAnonRss, deltaSwapRss, time, lastCompactAction,
- lastCompactTime, lastOomAdj, procState, zramFreeKbBefore,
- zramFreeKbAfter - zramFreeKbBefore);
- // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
- // on every single compaction for a flag that will seldom change and the
- // impact of reading the wrong value here is low.
- if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
- requestedAction, rssBefore[RSS_TOTAL_INDEX],
- rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
- rssBefore[RSS_SWAP_INDEX], rssAfter[RSS_TOTAL_INDEX],
- rssAfter[RSS_FILE_INDEX], rssAfter[RSS_ANON_INDEX],
- rssAfter[RSS_SWAP_INDEX], time, lastCompactAction,
- lastCompactTime, lastOomAdj,
- ActivityManager.processStateAmToProto(procState),
- zramFreeKbBefore, zramFreeKbAfter);
+ switch (opt.getReqCompactProfile()) {
+ case SOME:
+ ++perSourceStats.mSomeCompactPerformed;
+ ++perProcessStats.mSomeCompactPerformed;
+ break;
+ case FULL:
+ ++perSourceStats.mFullCompactPerformed;
+ ++perProcessStats.mFullCompactPerformed;
+ long anonRssSavings = -deltaAnonRss;
+ long zramConsumed = zramUsedKbAfter - zramUsedKbBefore;
+ long memFreed = anonRssSavings - zramConsumed;
+ long totalCpuTimeMillis = deltaCpuTimeNanos / 1000000;
+ long origAnonRss = rssBefore[RSS_ANON_INDEX];
+
+ // Negative stats would skew averages and will likely be due to
+ // noise of system doing other things so we put a floor at 0 to
+ // avoid negative values.
+ anonRssSavings = anonRssSavings > 0 ? anonRssSavings : 0;
+ zramConsumed = zramConsumed > 0 ? zramConsumed : 0;
+ memFreed = memFreed > 0 ? memFreed : 0;
+
+ perProcessStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis);
+ perSourceStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis);
+ SingleCompactionStats memStats = new SingleCompactionStats(rssAfter,
+ compactSource, name, anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis, procState, newOomAdj,
+ oomAdjReason, proc.uid);
+ mLastCompactionStats.remove(pid);
+ mLastCompactionStats.put(pid, memStats);
+ mCompactionStatsHistory.add(memStats);
+ memStats.sendStat();
+ break;
+ default:
+ // We likely missed adding this category, it needs to be added
+ // if we end up here. In the meantime, gracefully fallback to
+ // attribute to app.
+ Slog.wtf(TAG_AM, "Compaction: Unknown requested action");
+ return;
}
+ EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name,
+ resolvedAction.name(), rssBefore[RSS_TOTAL_INDEX],
+ rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
+ rssBefore[RSS_SWAP_INDEX], deltaTotalRss, deltaFileRss,
+ deltaAnonRss, deltaSwapRss, time, lastCompactProfile.name(),
+ lastCompactTime, newOomAdj, procState, zramUsedKbBefore,
+ zramUsedKbBefore - zramUsedKbAfter);
synchronized (mProcLock) {
opt.setLastCompactTime(end);
- opt.setLastCompactAction(resolvedAction);
- }
- if (resolvedAction == COMPACT_ACTION_FULL
- || resolvedAction == COMPACT_ACTION_ANON) {
- // Remove entry and insert again to update insertion order.
- mLastCompactionStats.remove(pid);
- mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
+ opt.setLastCompactProfile(requestedProfile);
}
} catch (Exception e) {
// nothing to do, presumably the process died
@@ -1603,7 +1860,10 @@
case COMPACT_SYSTEM_MSG: {
++mSystemCompactionsPerformed;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
+ long memFreedBefore = getMemoryFreedCompaction();
compactSystem();
+ long memFreedAfter = getMemoryFreedCompaction();
+ mSystemTotalMemFreed += memFreedAfter - memFreedBefore;
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
@@ -1809,14 +2069,14 @@
// Compact process.
@Override
- public void performCompaction(String action, int pid) throws IOException {
+ public void performCompaction(CompactAction action, int pid) throws IOException {
mPidCompacting = pid;
- if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
- } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
- } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
- compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
+ if (action == CompactAction.ALL) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+ } else if (action == CompactAction.FILE) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+ } else if (action == CompactAction.ANON) {
+ compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
}
mPidCompacting = -1;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9607e40..8759f230 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -76,9 +76,11 @@
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.AppProtoEnums;
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
@@ -113,6 +115,8 @@
import com.android.server.wm.WindowProcessController;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -122,20 +126,97 @@
*/
public class OomAdjuster {
static final String TAG = "OomAdjuster";
- static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
- static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
- static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
- static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver";
- static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver";
- static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService";
- static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService";
- static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService";
- static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
- static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
- static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
- static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange";
- static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
- static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
+
+ static final int OOM_ADJ_REASON_NONE = 0;
+ static final int OOM_ADJ_REASON_ACTIVITY = 1;
+ static final int OOM_ADJ_REASON_FINISH_RECEIVER = 2;
+ static final int OOM_ADJ_REASON_START_RECEIVER = 3;
+ static final int OOM_ADJ_REASON_BIND_SERVICE = 4;
+ static final int OOM_ADJ_REASON_UNBIND_SERVICE = 5;
+ static final int OOM_ADJ_REASON_START_SERVICE = 6;
+ static final int OOM_ADJ_REASON_GET_PROVIDER = 7;
+ static final int OOM_ADJ_REASON_REMOVE_PROVIDER = 8;
+ static final int OOM_ADJ_REASON_UI_VISIBILITY = 9;
+ static final int OOM_ADJ_REASON_ALLOWLIST = 10;
+ static final int OOM_ADJ_REASON_PROCESS_BEGIN = 11;
+ static final int OOM_ADJ_REASON_PROCESS_END = 12;
+
+ @IntDef(prefix = {"OOM_ADJ_REASON_"},
+ value = {OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, OOM_ADJ_REASON_FINISH_RECEIVER,
+ OOM_ADJ_REASON_START_RECEIVER, OOM_ADJ_REASON_BIND_SERVICE,
+ OOM_ADJ_REASON_UNBIND_SERVICE, OOM_ADJ_REASON_START_SERVICE,
+ OOM_ADJ_REASON_GET_PROVIDER, OOM_ADJ_REASON_REMOVE_PROVIDER,
+ OOM_ADJ_REASON_UI_VISIBILITY, OOM_ADJ_REASON_ALLOWLIST,
+ OOM_ADJ_REASON_PROCESS_BEGIN, OOM_ADJ_REASON_PROCESS_END})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OomAdjReason {}
+
+ public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) {
+ switch (oomReason) {
+ case OOM_ADJ_REASON_NONE:
+ return AppProtoEnums.OOM_ADJ_REASON_NONE;
+ case OOM_ADJ_REASON_ACTIVITY:
+ return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY;
+ case OOM_ADJ_REASON_FINISH_RECEIVER:
+ return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER;
+ case OOM_ADJ_REASON_START_RECEIVER:
+ return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER;
+ case OOM_ADJ_REASON_BIND_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE;
+ case OOM_ADJ_REASON_UNBIND_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE;
+ case OOM_ADJ_REASON_START_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE;
+ case OOM_ADJ_REASON_GET_PROVIDER:
+ return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER;
+ case OOM_ADJ_REASON_REMOVE_PROVIDER:
+ return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER;
+ case OOM_ADJ_REASON_UI_VISIBILITY:
+ return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY;
+ case OOM_ADJ_REASON_ALLOWLIST:
+ return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST;
+ case OOM_ADJ_REASON_PROCESS_BEGIN:
+ return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN;
+ case OOM_ADJ_REASON_PROCESS_END:
+ return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END;
+ default:
+ return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
+ }
+ }
+
+ public static final String oomAdjReasonToString(@OomAdjReason int oomReason) {
+ final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
+ switch (oomReason) {
+ case OOM_ADJ_REASON_NONE:
+ return OOM_ADJ_REASON_METHOD + "_meh";
+ case OOM_ADJ_REASON_ACTIVITY:
+ return OOM_ADJ_REASON_METHOD + "_activityChange";
+ case OOM_ADJ_REASON_FINISH_RECEIVER:
+ return OOM_ADJ_REASON_METHOD + "_finishReceiver";
+ case OOM_ADJ_REASON_START_RECEIVER:
+ return OOM_ADJ_REASON_METHOD + "_startReceiver";
+ case OOM_ADJ_REASON_BIND_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_bindService";
+ case OOM_ADJ_REASON_UNBIND_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_unbindService";
+ case OOM_ADJ_REASON_START_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_startService";
+ case OOM_ADJ_REASON_GET_PROVIDER:
+ return OOM_ADJ_REASON_METHOD + "_getProvider";
+ case OOM_ADJ_REASON_REMOVE_PROVIDER:
+ return OOM_ADJ_REASON_METHOD + "_removeProvider";
+ case OOM_ADJ_REASON_UI_VISIBILITY:
+ return OOM_ADJ_REASON_METHOD + "_uiVisibility";
+ case OOM_ADJ_REASON_ALLOWLIST:
+ return OOM_ADJ_REASON_METHOD + "_allowlistChange";
+ case OOM_ADJ_REASON_PROCESS_BEGIN:
+ return OOM_ADJ_REASON_METHOD + "_processBegin";
+ case OOM_ADJ_REASON_PROCESS_END:
+ return OOM_ADJ_REASON_METHOD + "_processEnd";
+ default:
+ return "_unknown";
+ }
+ }
/**
* Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
@@ -418,14 +499,14 @@
* Update OomAdj for all processes in LRU list
*/
@GuardedBy("mService")
- void updateOomAdjLocked(String oomAdjReason) {
+ void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
updateOomAdjLSP(oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
- private void updateOomAdjLSP(String oomAdjReason) {
+ private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) {
if (checkAndEnqueueOomAdjTargetLocked(null)) {
// Simply return as there is an oomAdjUpdate ongoing
return;
@@ -441,7 +522,7 @@
}
@GuardedBy({"mService", "mProcLock"})
- private void performUpdateOomAdjLSP(String oomAdjReason) {
+ private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
@@ -458,14 +539,14 @@
* @param oomAdjReason
*/
@GuardedBy("mService")
- boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
return updateOomAdjLSP(app, oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
- private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+ private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
updateOomAdjLSP(oomAdjReason);
return true;
@@ -487,10 +568,10 @@
}
@GuardedBy({"mService", "mProcLock"})
- private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+ private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
mAdjSeq++;
@@ -509,6 +590,7 @@
state.setCurBoundByNonBgRestrictedApp(false);
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
+ app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp,
SystemClock.uptimeMillis());
// The 'app' here itself might or might not be in the cycle, for example,
@@ -688,7 +770,7 @@
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("mService")
- void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
// First check if there is pending full update
if (mPendingFullOomAdjUpdate) {
mPendingFullOomAdjUpdate = false;
@@ -716,10 +798,11 @@
}
@GuardedBy("mService")
- private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ private void performUpdateOomAdjPendingTargetsLocked(
+ @OomAdjuster.OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
final ArrayList<ProcessRecord> processes = mTmpProcessList;
@@ -741,11 +824,11 @@
* get evaluated recursively here.
*/
@GuardedBy({"mService", "mProcLock"})
- private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
+ private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
boolean startProfiling) {
if (startProfiling) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
}
final long now = SystemClock.uptimeMillis();
@@ -806,6 +889,7 @@
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null) {
state.setProcStateChanged(false);
+ app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
computeClients); // It won't enter cycle if not computing clients.
// if any app encountered a cycle, we need to perform an additional loop later
@@ -2582,11 +2666,13 @@
// processing of the requests. As a result, there is throttling both here
// and in CachedAppOptimizer.
&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
- mCachedAppOptimizer.compactAppPersistent(app);
+ mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.PERSISTENT, false);
} else if (state.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
- mCachedAppOptimizer.compactAppBfgs(app);
+ mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.BFGS, false);
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a613729..fb41a39 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -39,16 +39,24 @@
private long mLastCompactTime;
/**
- * The most recent compaction action requested for this app.
+ * The most recent compaction profile requested for this app.
*/
- @GuardedBy("mProcLock")
- private int mReqCompactAction;
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mReqCompactProfile;
+
+ /**
+ * Source that requested the latest compaction for this app.
+ */
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactSource mReqCompactSource;
+
+ /**
+ * Last oom adjust change reason for this app.
+ */
+ @GuardedBy("mProcLock") private @OomAdjuster.OomAdjReason int mLastOomAdjChangeReason;
/**
* The most recent compaction action performed for this app.
*/
- @GuardedBy("mProcLock")
- private int mLastCompactAction;
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mLastCompactProfile;
/**
* This process has been scheduled for a memory compaction.
@@ -105,23 +113,49 @@
}
@GuardedBy("mProcLock")
- int getReqCompactAction() {
- return mReqCompactAction;
+ CachedAppOptimizer.CompactProfile getReqCompactProfile() {
+ return mReqCompactProfile;
}
@GuardedBy("mProcLock")
- void setReqCompactAction(int reqCompactAction) {
- mReqCompactAction = reqCompactAction;
+ void setReqCompactProfile(CachedAppOptimizer.CompactProfile reqCompactProfile) {
+ mReqCompactProfile = reqCompactProfile;
}
@GuardedBy("mProcLock")
- int getLastCompactAction() {
- return mLastCompactAction;
+ CachedAppOptimizer.CompactSource getReqCompactSource() {
+ return mReqCompactSource;
}
@GuardedBy("mProcLock")
- void setLastCompactAction(int lastCompactAction) {
- mLastCompactAction = lastCompactAction;
+ void setReqCompactSource(CachedAppOptimizer.CompactSource stat) {
+ mReqCompactSource = stat;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastOomAdjChangeReason(@OomAdjuster.OomAdjReason int reason) {
+ mLastOomAdjChangeReason = reason;
+ }
+
+ @GuardedBy("mProcLock")
+ @OomAdjuster.OomAdjReason
+ int getLastOomAdjChangeReason() {
+ return mLastOomAdjChangeReason;
+ }
+
+ @GuardedBy("mProcLock")
+ CachedAppOptimizer.CompactProfile getLastCompactProfile() {
+ if (mLastCompactProfile == null) {
+ // The first compaction won't have a previous one, so assign one to avoid crashing.
+ mLastCompactProfile = CachedAppOptimizer.CompactProfile.SOME;
+ }
+
+ return mLastCompactProfile;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastCompactProfile(CachedAppOptimizer.CompactProfile lastCompactProfile) {
+ mLastCompactProfile = lastCompactProfile;
}
@GuardedBy("mProcLock")
@@ -216,7 +250,8 @@
@GuardedBy("mProcLock")
void dump(PrintWriter pw, String prefix, long nowUptime) {
pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
- pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+ pw.print(" lastCompactProfile=");
+ pw.println(mLastCompactProfile);
pw.print(prefix);
pw.print("hasPendingCompaction=");
pw.print(mPendingCompact);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index dd73cbe..854b818 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -25,20 +25,6 @@
import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.Manifest;
import android.annotation.NonNull;
@@ -48,10 +34,10 @@
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
+import android.app.GameManagerInternal;
import android.app.GameModeInfo;
import android.app.GameState;
import android.app.IGameManagerService;
-import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -75,9 +61,7 @@
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -92,8 +76,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.compat.CompatibilityOverrideConfig;
-import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -140,10 +122,6 @@
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
- static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
- .build();
- static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
- .build();
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
@@ -156,7 +134,6 @@
private final Handler mHandler;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
- private final IPlatformCompat mPlatformCompat;
private final PowerManagerInternal mPowerManagerInternal;
private final File mSystemDir;
@VisibleForTesting
@@ -180,8 +157,6 @@
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
- mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(Environment.getDataDirectory(), "system");
mSystemDir.mkdirs();
@@ -212,8 +187,6 @@
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
- mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
@@ -326,14 +299,10 @@
break;
}
case POPULATE_GAME_MODE_SETTINGS: {
- // Scan all game packages and re-enforce the configured compat mode overrides
- // as the DeviceConfig may have be wiped/since last reboot and we can't risk
- // having overrides configured for packages that no longer have any DeviceConfig
- // and thus any way to escape compat mode.
removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
final int userId = (int) msg.obj;
final String[] packageNames = getInstalledGamePackageNames(userId);
- updateConfigsForUser(userId, packageNames);
+ updateConfigsForUser(userId, false /*checkGamePackage*/, packageNames);
break;
}
case SET_GAME_STATE: {
@@ -402,7 +371,8 @@
@Override
public void onPropertiesChanged(Properties properties) {
final String[] packageNames = properties.getKeyset().toArray(new String[0]);
- updateConfigsForUser(ActivityManager.getCurrentUser(), packageNames);
+ updateConfigsForUser(ActivityManager.getCurrentUser(), true /*checkGamePackage*/,
+ packageNames);
}
@Override
@@ -411,39 +381,6 @@
}
}
- // Turn the raw string to the corresponding CompatChange id.
- static long getCompatChangeId(String raw) {
- switch (raw) {
- case "0.3":
- return DOWNSCALE_30;
- case "0.35":
- return DOWNSCALE_35;
- case "0.4":
- return DOWNSCALE_40;
- case "0.45":
- return DOWNSCALE_45;
- case "0.5":
- return DOWNSCALE_50;
- case "0.55":
- return DOWNSCALE_55;
- case "0.6":
- return DOWNSCALE_60;
- case "0.65":
- return DOWNSCALE_65;
- case "0.7":
- return DOWNSCALE_70;
- case "0.75":
- return DOWNSCALE_75;
- case "0.8":
- return DOWNSCALE_80;
- case "0.85":
- return DOWNSCALE_85;
- case "0.9":
- return DOWNSCALE_90;
- }
- return 0;
- }
-
public enum FrameRate {
FPS_DEFAULT(0),
FPS_30(30),
@@ -553,7 +490,9 @@
private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
private final String mPackageName;
- private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
+ private final Object mModeConfigLock = new Object();
+ @GuardedBy("mModeConfigLock")
+ private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs = new ArrayMap<>();
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
@@ -562,7 +501,6 @@
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
- mModeConfigs = new ArrayMap<>();
try {
final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
@@ -647,6 +585,13 @@
return xmlFound;
}
+ GameModeConfiguration getOrAddDefaultGameModeConfiguration(int gameMode) {
+ synchronized (mModeConfigLock) {
+ mModeConfigs.putIfAbsent(gameMode, new GameModeConfiguration(gameMode));
+ return mModeConfigs.get(gameMode);
+ }
+ }
+
/**
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
@@ -657,17 +602,26 @@
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
public static final String FPS_KEY = "fps";
- public static final String DEFAULT_SCALING = "1.0";
- public static final String DEFAULT_FPS = "";
public static final String ANGLE_KEY = "useAngle";
public static final String LOADING_BOOST_KEY = "loadingBoost";
+ public static final float DEFAULT_SCALING = -1f;
+ public static final String DEFAULT_FPS = "";
+ public static final boolean DEFAULT_USE_ANGLE = false;
+ public static final int DEFAULT_LOADING_BOOST_DURATION = -1;
+
private final @GameMode int mGameMode;
- private String mScaling;
- private String mFps;
+ private float mScaling = DEFAULT_SCALING;
+ private String mFps = DEFAULT_FPS;
private final boolean mUseAngle;
private final int mLoadingBoostDuration;
+ GameModeConfiguration(int gameMode) {
+ mGameMode = gameMode;
+ mUseAngle = DEFAULT_USE_ANGLE;
+ mLoadingBoostDuration = DEFAULT_LOADING_BOOST_DURATION;
+ }
+
GameModeConfiguration(KeyValueListParser parser) {
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
// isGameModeOptedIn() returns if an app will handle all of the changes necessary
@@ -675,7 +629,7 @@
// GameManagerService) will not do anything for the app (like window scaling or
// using ANGLE).
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
- ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+ ? DEFAULT_SCALING : parser.getFloat(SCALING_KEY, DEFAULT_SCALING);
mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
@@ -684,21 +638,22 @@
// - The app has not opted in to performing the work itself AND
// - The Phenotype config has enabled it.
mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
- && parser.getBoolean(ANGLE_KEY, false);
+ && parser.getBoolean(ANGLE_KEY, DEFAULT_USE_ANGLE);
- mLoadingBoostDuration = willGamePerformOptimizations(mGameMode) ? -1
- : parser.getInt(LOADING_BOOST_KEY, -1);
+ mLoadingBoostDuration = willGamePerformOptimizations(mGameMode)
+ ? DEFAULT_LOADING_BOOST_DURATION
+ : parser.getInt(LOADING_BOOST_KEY, DEFAULT_LOADING_BOOST_DURATION);
}
public int getGameMode() {
return mGameMode;
}
- public String getScaling() {
+ public synchronized float getScaling() {
return mScaling;
}
- public int getFps() {
+ public synchronized int getFps() {
return GameManagerService.getFpsInt(mFps);
}
@@ -710,15 +665,15 @@
return mLoadingBoostDuration;
}
- public void setScaling(String scaling) {
+ public synchronized void setScaling(float scaling) {
mScaling = scaling;
}
- public void setFpsStr(String fpsStr) {
+ public synchronized void setFpsStr(String fpsStr) {
mFps = fpsStr;
}
- public boolean isValid() {
+ public boolean isActive() {
return (mGameMode == GameManager.GAME_MODE_STANDARD
|| mGameMode == GameManager.GAME_MODE_PERFORMANCE
|| mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -733,13 +688,6 @@
+ mUseAngle + ",Fps:" + mFps + ",Loading Boost Duration:"
+ mLoadingBoostDuration + "]";
}
-
- /**
- * Get the corresponding compat change id for the current scaling string.
- */
- public long getCompatChangeId() {
- return GameManagerService.getCompatChangeId(mScaling);
- }
}
public String getPackageName() {
@@ -761,8 +709,10 @@
private int getAvailableGameModesBitfield() {
int field = 0;
- for (final int mode : mModeConfigs.keySet()) {
- field |= modeToBitmask(mode);
+ synchronized (mModeConfigLock) {
+ for (final int mode : mModeConfigs.keySet()) {
+ field |= modeToBitmask(mode);
+ }
}
if (mBatteryModeOptedIn) {
field |= modeToBitmask(GameManager.GAME_MODE_BATTERY);
@@ -803,27 +753,43 @@
* @return The package's GameModeConfiguration for the provided mode or null if absent
*/
public GameModeConfiguration getGameModeConfiguration(@GameMode int gameMode) {
- return mModeConfigs.get(gameMode);
+ synchronized (mModeConfigLock) {
+ return mModeConfigs.get(gameMode);
+ }
}
/**
- * Insert a new GameModeConfiguration
+ * Inserts a new GameModeConfiguration
*/
public void addModeConfig(GameModeConfiguration config) {
- if (config.isValid()) {
- mModeConfigs.put(config.getGameMode(), config);
+ if (config.isActive()) {
+ synchronized (mModeConfigLock) {
+ mModeConfigs.put(config.getGameMode(), config);
+ }
} else {
- Slog.w(TAG, "Invalid game mode config for "
+ Slog.w(TAG, "Attempt to add inactive game mode config for "
+ mPackageName + ":" + config.toString());
}
}
- public boolean isValid() {
- return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+ public boolean isActive() {
+ synchronized (mModeConfigLock) {
+ return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+ }
}
public String toString() {
- return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+ synchronized (mModeConfigLock) {
+ return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+ }
+ }
+ }
+
+ private final class LocalService extends GameManagerInternal {
+ @Override
+ public float getResolutionScalingFactor(String packageName, int userId) {
+ final int gameMode = getGameModeFromSettings(packageName, userId);
+ return getResolutionScalingFactorInternal(packageName, gameMode, userId);
}
}
@@ -837,13 +803,13 @@
public Lifecycle(Context context) {
super(context);
+ mService = new GameManagerService(context);
}
@Override
public void onStart() {
- final Context context = getContext();
- mService = new GameManagerService(context);
publishBinderService(Context.GAME_SERVICE, mService);
+ mService.publishLocalService();
mService.registerDeviceConfigListener();
mService.registerPackageReceiver();
}
@@ -894,15 +860,7 @@
}
private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
- GamePackageConfiguration config = null;
- synchronized (mOverrideConfigLock) {
- config = mOverrideConfigs.get(packageName);
- }
- if (config == null) {
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- }
+ final GamePackageConfiguration config = getConfig(packageName);
if (config == null) {
return new int[]{};
}
@@ -933,8 +891,8 @@
private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
- Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
- + " selected for package: '" + packageName + "'");
+ Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ + " selected for package: '" + packageName + "'");
return GameManager.GAME_MODE_UNSUPPORTED;
}
@@ -1055,19 +1013,19 @@
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
return false;
}
-
+ final GamePackageConfiguration config;
synchronized (mDeviceConfigLock) {
- final GamePackageConfiguration config = mConfigs.get(packageName);
+ config = mConfigs.get(packageName);
if (config == null) {
return false;
}
- GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
- config.getGameModeConfiguration(gameMode);
- if (gameModeConfiguration == null) {
- return false;
- }
- return gameModeConfiguration.getUseAngle();
}
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return false;
+ }
+ return gameModeConfiguration.getUseAngle();
}
/**
@@ -1082,19 +1040,19 @@
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
return -1;
}
-
+ final GamePackageConfiguration config;
synchronized (mDeviceConfigLock) {
- final GamePackageConfiguration config = mConfigs.get(packageName);
- if (config == null) {
- return -1;
- }
- GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
- config.getGameModeConfiguration(gameMode);
- if (gameModeConfiguration == null) {
- return -1;
- }
- return gameModeConfiguration.getLoadingBoostDuration();
+ config = mConfigs.get(packageName);
}
+ if (config == null) {
+ return -1;
+ }
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return -1;
+ }
+ return gameModeConfiguration.getLoadingBoostDuration();
}
/**
@@ -1159,6 +1117,66 @@
mGameServiceController.setGameServiceProvider(packageName);
}
+
+ /**
+ * Updates the resolution scaling factor for the package's target game mode and activates it.
+ *
+ * @param scalingFactor enable scaling override over any other compat scaling if positive,
+ * or disable the override otherwise
+ * @throws SecurityException if caller doesn't have
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}
+ * permission.
+ * @throws IllegalArgumentException if the user ID provided doesn't exist.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor,
+ int userId) throws SecurityException, IllegalArgumentException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ throw new IllegalArgumentException("User " + userId + " wasn't started");
+ }
+ }
+ setGameModeConfigOverride(packageName, userId, gameMode, null /*fpsStr*/,
+ Float.toString(scalingFactor));
+ }
+
+ /**
+ * Gets the resolution scaling factor for the package's target game mode.
+ *
+ * @return scaling factor for the game mode if exists or negative value otherwise.
+ * @throws SecurityException if caller doesn't have
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}
+ * permission.
+ * @throws IllegalArgumentException if the user ID provided doesn't exist.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public float getResolutionScalingFactor(String packageName, int gameMode, int userId)
+ throws SecurityException, IllegalArgumentException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ throw new IllegalArgumentException("User " + userId + " wasn't started");
+ }
+ }
+ return getResolutionScalingFactorInternal(packageName, gameMode, userId);
+ }
+
+ float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
+ if (packageConfig == null) {
+ return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+ }
+ final GamePackageConfiguration.GameModeConfiguration modeConfig =
+ packageConfig.getGameModeConfiguration(gameMode);
+ if (modeConfig != null) {
+ return modeConfig.getScaling();
+ }
+ return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+ }
+
/**
* Notified when boot is completed.
*/
@@ -1235,28 +1253,6 @@
}
/**
- * @hide
- */
- @VisibleForTesting
- public void disableCompatScale(String packageName) {
- final long uid = Binder.clearCallingIdentity();
- try {
- Slog.i(TAG, "Disabling downscale for " + packageName);
- final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
- overrides.put(DOWNSCALED, COMPAT_DISABLED);
- final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
- overrides);
- try {
- mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
- }
- } finally {
- Binder.restoreCallingIdentity(uid);
- }
- }
-
- /**
* Remove frame rate override due to mode switch
*/
private void resetFps(String packageName, @UserIdInt int userId) {
@@ -1269,60 +1265,6 @@
}
}
- private void enableCompatScale(String packageName, long scaleId) {
- final long uid = Binder.clearCallingIdentity();
- try {
- Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
- final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
- overrides.put(DOWNSCALED, COMPAT_ENABLED);
- overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
- overrides.put(scaleId, COMPAT_ENABLED);
- final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
- overrides);
- try {
- mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
- }
- } finally {
- Binder.restoreCallingIdentity(uid);
- }
- }
-
- private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
- String packageName, @GameMode int gameMode) {
-
- if (DEBUG) {
- Slog.v(TAG, dumpDeviceConfigs());
- }
- final GamePackageConfiguration.GameModeConfiguration modeConfig =
- packageConfig.getGameModeConfiguration(gameMode);
- if (modeConfig == null) {
- Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
- return;
- }
- long scaleId = modeConfig.getCompatChangeId();
- if (scaleId == 0) {
- Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
- + packageName);
- return;
- }
-
- enableCompatScale(packageName, scaleId);
- }
-
private int modeToBitmask(@GameMode int gameMode) {
return (1 << gameMode);
}
@@ -1360,31 +1302,17 @@
@GameMode int gameMode, @UserIdInt int userId) {
if (gameMode == GameManager.GAME_MODE_STANDARD
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
- disableCompatScale(packageName);
resetFps(packageName, userId);
return;
}
- GamePackageConfiguration packageConfig = null;
-
- synchronized (mOverrideConfigLock) {
- packageConfig = mOverrideConfigs.get(packageName);
- }
-
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
if (packageConfig == null) {
- synchronized (mDeviceConfigLock) {
- packageConfig = mConfigs.get(packageName);
- }
- }
-
- if (packageConfig == null) {
- disableCompatScale(packageName);
Slog.v(TAG, "Package configuration not found for " + packageName);
return;
}
if (packageConfig.willGamePerformOptimizations(gameMode)) {
return;
}
- updateCompatModeDownscale(packageConfig, packageName, gameMode);
updateFps(packageConfig, packageName, gameMode, userId);
updateUseAngle(packageName, gameMode);
}
@@ -1404,34 +1332,31 @@
}
}
// Adding override game mode configuration of the given package name
+ GamePackageConfiguration overrideConfig;
synchronized (mOverrideConfigLock) {
// look for the existing override GamePackageConfiguration
- GamePackageConfiguration overrideConfig = mOverrideConfigs.get(packageName);
+ overrideConfig = mOverrideConfigs.get(packageName);
if (overrideConfig == null) {
overrideConfig = new GamePackageConfiguration(packageName, userId);
mOverrideConfigs.put(packageName, overrideConfig);
}
-
- // modify GameModeConfiguration intervention settings
- GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
- overrideConfig.getGameModeConfiguration(gameMode);
-
- if (fpsStr != null) {
- overrideModeConfig.setFpsStr(fpsStr);
- } else {
- overrideModeConfig.setFpsStr(
- GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
- }
- if (scaling != null) {
- overrideModeConfig.setScaling(scaling);
- } else {
- overrideModeConfig.setScaling(
- GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
- }
- Slog.i(TAG, "Package Name: " + packageName
- + " FPS: " + String.valueOf(overrideModeConfig.getFps())
- + " Scaling: " + overrideModeConfig.getScaling());
}
+ // modify GameModeConfiguration intervention settings
+ GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
+ overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
+
+ if (fpsStr != null) {
+ overrideModeConfig.setFpsStr(fpsStr);
+ } else {
+ overrideModeConfig.setFpsStr(
+ GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
+ }
+ if (scaling != null) {
+ overrideModeConfig.setScaling(Float.parseFloat(scaling));
+ }
+ Slog.i(TAG, "Package Name: " + packageName
+ + " FPS: " + String.valueOf(overrideModeConfig.getFps())
+ + " Scaling: " + overrideModeConfig.getScaling());
setGameMode(packageName, gameMode, userId);
}
@@ -1477,7 +1402,7 @@
}
// If the game mode to reset is the only mode other than standard mode,
- // The override config is removed.
+ // the override config is removed.
if (modes.length <= 2) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1497,15 +1422,7 @@
// If not, set the game mode to standard
int gameMode = getGameMode(packageName, userId);
- GamePackageConfiguration config = null;
- synchronized (mOverrideConfigLock) {
- config = mOverrideConfigs.get(packageName);
- }
- if (config == null) {
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- }
+ final GamePackageConfiguration config = getConfig(packageName);
final int newGameMode = getNewGameMode(gameMode, config);
if (gameMode != newGameMode) {
setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
@@ -1544,18 +1461,8 @@
* Returns the string listing all the interventions currently set to a game.
*/
public String getInterventionList(String packageName) {
- GamePackageConfiguration packageConfig = null;
- synchronized (mOverrideConfigLock) {
- packageConfig = mOverrideConfigs.get(packageName);
- }
-
- if (packageConfig == null) {
- synchronized (mDeviceConfigLock) {
- packageConfig = mConfigs.get(packageName);
- }
- }
-
- StringBuilder listStrSb = new StringBuilder();
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
+ final StringBuilder listStrSb = new StringBuilder();
if (packageConfig == null) {
listStrSb.append("\n No intervention found for package ")
.append(packageName);
@@ -1570,20 +1477,27 @@
* @hide
*/
@VisibleForTesting
- void updateConfigsForUser(@UserIdInt int userId, String... packageNames) {
+ void updateConfigsForUser(@UserIdInt int userId, boolean checkGamePackage,
+ String... packageNames) {
+ if (checkGamePackage) {
+ packageNames = Arrays.stream(packageNames).filter(
+ p -> isPackageGame(p, userId)).toArray(String[]::new);
+ }
try {
synchronized (mDeviceConfigLock) {
for (final String packageName : packageNames) {
final GamePackageConfiguration config =
new GamePackageConfiguration(packageName, userId);
- if (config.isValid()) {
+ if (config.isActive()) {
if (DEBUG) {
Slog.i(TAG, "Adding config: " + config.toString());
}
mConfigs.put(packageName, config);
} else {
- Slog.w(TAG, "Invalid package config for "
- + config.getPackageName() + ":" + config.toString());
+ if (DEBUG) {
+ Slog.w(TAG, "Inactive package config for "
+ + config.getPackageName() + ":" + config.toString());
+ }
mConfigs.remove(packageName);
}
}
@@ -1613,7 +1527,7 @@
}
}
} catch (Exception e) {
- Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
+ Slog.e(TAG, "Failed to update configs for user " + userId + ": " + e);
}
final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
@@ -1664,7 +1578,7 @@
final int useAngle = gameModeConfiguration.getUseAngle() ? 1 : 0;
sb.append(TextUtils.formatSimple("angle=%d", useAngle));
sb.append(",");
- final String scaling = gameModeConfiguration.getScaling();
+ final float scaling = gameModeConfiguration.getScaling();
sb.append("scaling=");
sb.append(scaling);
sb.append(",");
@@ -1761,17 +1675,9 @@
}
switch (intent.getAction()) {
case ACTION_PACKAGE_ADDED:
- updateConfigsForUser(userId, packageName);
+ updateConfigsForUser(userId, true /*checkGamePackage*/, packageName);
break;
case ACTION_PACKAGE_REMOVED:
- disableCompatScale(packageName);
- // If EXTRA_REPLACING is true, it means there will be an
- // ACTION_PACKAGE_ADDED triggered after this because this
- // is an updated package that gets installed. Hence, disable
- // resolution downscaling effort but avoid removing the server
- // or commandline overriding configurations because those will
- // not change but the package game mode configurations may change
- // which may opt in and/or opt out some game mode configurations.
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1803,6 +1709,10 @@
mDeviceConfigListener = new DeviceConfigListener();
}
+ private void publishLocalService() {
+ LocalServices.addService(GameManagerInternal.class, new LocalService());
+ }
+
private String dumpDeviceConfigs() {
StringBuilder out = new StringBuilder();
for (String key : mConfigs.keySet()) {
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 470c320..487d19a 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,21 +16,6 @@
package com.android.server.app;
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
-
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
@@ -39,7 +24,6 @@
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.ShellCommand;
-import android.util.ArraySet;
import java.io.PrintWriter;
import java.util.Locale;
@@ -53,23 +37,6 @@
public GameManagerShellCommand() {}
- private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
- DOWNSCALED,
- DOWNSCALE_90,
- DOWNSCALE_85,
- DOWNSCALE_80,
- DOWNSCALE_75,
- DOWNSCALE_70,
- DOWNSCALE_65,
- DOWNSCALE_60,
- DOWNSCALE_55,
- DOWNSCALE_50,
- DOWNSCALE_45,
- DOWNSCALE_40,
- DOWNSCALE_35,
- DOWNSCALE_30,
- });
-
@Override
public int onCommand(String cmd) {
if (cmd == null) {
@@ -212,11 +179,15 @@
case "--downscale":
if (downscaleRatio == null) {
downscaleRatio = getNextArgRequired();
- if (downscaleRatio != null
- && GameManagerService.getCompatChangeId(downscaleRatio) == 0
- && !downscaleRatio.equals("disable")) {
- pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
- return -1;
+ if ("disable".equals(downscaleRatio)) {
+ downscaleRatio = "-1";
+ } else {
+ try {
+ Float.parseFloat(downscaleRatio);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
+ return -1;
+ }
}
} else {
pw.println("Duplicate option '" + option + "'");
@@ -249,40 +220,16 @@
final GameManagerService gameManagerService = (GameManagerService)
ServiceManager.getService(Context.GAME_SERVICE);
- boolean batteryModeSupported = false;
- boolean perfModeSupported = false;
- int [] modes = gameManagerService.getAvailableGameModes(packageName);
-
- for (int mode : modes) {
- if (mode == GameManager.GAME_MODE_PERFORMANCE) {
- perfModeSupported = true;
- } else if (mode == GameManager.GAME_MODE_BATTERY) {
- batteryModeSupported = true;
- }
- }
-
switch (gameMode.toLowerCase(Locale.getDefault())) {
case "2":
case "performance":
- if (perfModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
break;
case "3":
case "battery":
- if (batteryModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
break;
default:
pw.println("Invalid game mode: " + gameMode);
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98..25a4f37 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
package com.android.server.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@
runningTaskInfos = mActivityTaskManager.getTasks(
/* maxNum= */ Integer.MAX_VALUE,
/* filterOnlyVisibleRecents= */ false,
- /* keepIntentExtra= */ false);
+ /* keepIntentExtra= */ false,
+ INVALID_DISPLAY);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to fetch running tasks");
return null;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7bef6d5..a5bcb05 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -217,7 +217,7 @@
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
-public class AppOpsService extends IAppOpsService.Stub {
+public class AppOpsService extends IAppOpsService.Stub implements PersistenceScheduler {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
@@ -402,6 +402,9 @@
/** Package Manager internal. Access via {@link #getPackageManagerInternal()} */
private @Nullable PackageManagerInternal mPackageManagerInternal;
+ /** Interface for app-op modes.*/
+ @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface;
+
/**
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
*/
@@ -558,7 +561,6 @@
public boolean pendingAppWidgetVisible;
public ArrayMap<String, Ops> pkgOps;
- public SparseIntArray opModes;
// true indicates there is an interested observer, false there isn't but it has such an op
public SparseBooleanArray foregroundOps;
@@ -569,15 +571,43 @@
}
public void clear() {
+ mAppOpsServiceInterface.removeUid(uid);
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ mAppOpsServiceInterface.removePackage(packageName);
+ }
+ }
pkgOps = null;
- opModes = null;
}
public boolean isDefault() {
+ boolean areAllPackageModesDefault = true;
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ if (!mAppOpsServiceInterface.arePackageModesDefault(packageName)) {
+ areAllPackageModesDefault = false;
+ break;
+ }
+ }
+ }
return (pkgOps == null || pkgOps.isEmpty())
- && (opModes == null || opModes.size() <= 0)
&& (state == UID_STATE_CACHED
- && (pendingState == UID_STATE_CACHED));
+ && (pendingState == UID_STATE_CACHED))
+ && (mAppOpsServiceInterface.areUidModesDefault(uid)
+ && areAllPackageModesDefault);
+ }
+
+ // Functions for uid mode access and manipulation.
+ public SparseIntArray getNonDefaultUidModes() {
+ return mAppOpsServiceInterface.getNonDefaultUidModes(uid);
+ }
+
+ public int getUidMode(int op) {
+ return mAppOpsServiceInterface.getUidMode(uid, op);
+ }
+
+ public boolean setUidMode(int op, int mode) {
+ return mAppOpsServiceInterface.setUidMode(uid, op, mode);
}
int evalMode(int op, int mode) {
@@ -647,21 +677,20 @@
public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
SparseBooleanArray which = null;
hasForegroundWatchers = false;
- if (opModes != null) {
- for (int i = opModes.size() - 1; i >= 0; i--) {
- if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
- }
- evalForegroundWatchers(opModes.keyAt(i), watchers, which);
+ final SparseIntArray opModes = getNonDefaultUidModes();
+ for (int i = opModes.size() - 1; i >= 0; i--) {
+ if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
+ if (which == null) {
+ which = new SparseBooleanArray();
}
+ evalForegroundWatchers(opModes.keyAt(i), watchers, which);
}
}
if (pkgOps != null) {
for (int i = pkgOps.size() - 1; i >= 0; i--) {
Ops ops = pkgOps.valueAt(i);
for (int j = ops.size() - 1; j >= 0; j--) {
- if (ops.valueAt(j).mode == AppOpsManager.MODE_FOREGROUND) {
+ if (ops.valueAt(j).getMode() == AppOpsManager.MODE_FOREGROUND) {
if (which == null) {
which = new SparseBooleanArray();
}
@@ -1451,8 +1480,6 @@
final UidState uidState;
final @NonNull String packageName;
- private @Mode int mode;
-
/** attributionTag -> AttributedOp */
final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
@@ -1461,15 +1488,17 @@
this.uid = uid;
this.uidState = uidState;
this.packageName = packageName;
- this.mode = AppOpsManager.opToDefaultMode(op);
}
- int getMode() {
- return mode;
+ @Mode int getMode() {
+ return mAppOpsServiceInterface.getPackageMode(packageName, this.op);
+ }
+ void setMode(@Mode int mode) {
+ mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode);
}
int evalMode() {
- return uidState.evalMode(op, mode);
+ return uidState.evalMode(op, getMode());
}
void removeAttributionsWithNoTime() {
@@ -1503,7 +1532,7 @@
mAttributions.valueAt(i).createAttributedOpEntryLocked());
}
- return new OpEntry(op, mode, attributionEntries);
+ return new OpEntry(op, getMode(), attributionEntries);
}
@NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
@@ -1518,7 +1547,7 @@
}
}
- return new OpEntry(op, mode, attributionEntries);
+ return new OpEntry(op, getMode(), attributionEntries);
}
boolean isRunning() {
@@ -1775,6 +1804,7 @@
public AppOpsService(File storagePath, Handler handler, Context context) {
mContext = context;
+ mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this);
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
@@ -1815,7 +1845,7 @@
if (uidState == null || uidState.pkgOps == null) {
return;
}
-
+ mAppOpsServiceInterface.removePackage(pkgName);
Ops removedOps = uidState.pkgOps.remove(pkgName);
if (removedOps != null) {
scheduleFastWriteLocked();
@@ -2037,25 +2067,27 @@
return;
}
- Ops ops = null;
+ Ops removedOps = null;
// Remove any package state if such.
if (uidState.pkgOps != null) {
- ops = uidState.pkgOps.remove(packageName);
+ removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName);
}
// If we just nuked the last package state check if the UID is valid.
- if (ops != null && uidState.pkgOps.isEmpty()
+ if (removedOps != null && uidState.pkgOps.isEmpty()
&& getPackagesForUid(uid).length <= 0) {
+ uidState.clear();
mUidStates.remove(uid);
}
- if (ops != null) {
+ if (removedOps != null) {
scheduleFastWriteLocked();
- final int numOps = ops.size();
+ final int numOps = removedOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
- final Op op = ops.valueAt(opNum);
+ final Op op = removedOps.valueAt(opNum);
final int numAttributions = op.mAttributions.size();
for (int attributionNum = 0; attributionNum < numAttributions;
@@ -2080,6 +2112,7 @@
public void uidRemoved(int uid) {
synchronized (this) {
if (mUidStates.indexOfKey(uid) >= 0) {
+ mUidStates.get(uid).clear();
mUidStates.remove(uid);
scheduleFastWriteLocked();
}
@@ -2185,12 +2218,11 @@
private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
- final long elapsedNow = SystemClock.elapsedRealtime();
if (ops == null) {
resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
- resOps.add(getOpEntryForResult(curOp, elapsedNow));
+ resOps.add(getOpEntryForResult(curOp));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -2199,7 +2231,7 @@
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(getOpEntryForResult(curOp, elapsedNow));
+ resOps.add(getOpEntryForResult(curOp));
}
}
}
@@ -2209,11 +2241,12 @@
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- if (uidState.opModes == null) {
+ final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes == null) {
return null;
}
- int opModeCount = uidState.opModes.size();
+ int opModeCount = opModes.size();
if (opModeCount == 0) {
return null;
}
@@ -2221,25 +2254,24 @@
if (ops == null) {
resOps = new ArrayList<>();
for (int i = 0; i < opModeCount; i++) {
- int code = uidState.opModes.keyAt(i);
- resOps.add(new OpEntry(code, uidState.opModes.get(code), Collections.emptyMap()));
+ int code = opModes.keyAt(i);
+ resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
}
} else {
for (int j=0; j<ops.length; j++) {
int code = ops[j];
- if (uidState.opModes.indexOfKey(code) >= 0) {
+ if (opModes.indexOfKey(code) >= 0) {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(new OpEntry(code, uidState.opModes.get(code),
- Collections.emptyMap()));
+ resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
}
}
}
return resOps;
}
- private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
+ private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
return op.createEntryLocked();
}
@@ -2484,15 +2516,18 @@
Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false);
if (ops != null) {
ops.remove(op.op);
+ op.setMode(AppOpsManager.opToDefaultMode(op.op));
if (ops.size() <= 0) {
UidState uidState = ops.uidState;
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (pkgOps != null) {
pkgOps.remove(ops.packageName);
+ mAppOpsServiceInterface.removePackage(ops.packageName);
if (pkgOps.isEmpty()) {
uidState.pkgOps = null;
}
if (uidState.isDefault()) {
+ uidState.clear();
mUidStates.remove(uid);
}
}
@@ -2548,33 +2583,18 @@
if (mode == defaultMode) {
return;
}
- previousMode = MODE_DEFAULT;
uidState = new UidState(uid);
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
mUidStates.put(uid, uidState);
- scheduleWriteLocked();
- } else if (uidState.opModes == null) {
- previousMode = MODE_DEFAULT;
- if (mode != defaultMode) {
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
- scheduleWriteLocked();
- }
+ }
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+ previousMode = uidState.getUidMode(code);
} else {
- if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
- return;
- }
- previousMode = uidState.opModes.get(code);
- if (mode == defaultMode) {
- uidState.opModes.delete(code);
- if (uidState.opModes.size() <= 0) {
- uidState.opModes = null;
- }
- } else {
- uidState.opModes.put(code, mode);
- }
- scheduleWriteLocked();
+ // doesn't look right but is legacy behavior.
+ previousMode = MODE_DEFAULT;
+ }
+
+ if (!uidState.setUidMode(code, mode)) {
+ return;
}
uidState.evalForegroundOps(mOpModeWatchers);
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2806,9 +2826,10 @@
UidState uidState = getUidStateLocked(uid, false);
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
if (op != null) {
- if (op.mode != mode) {
- previousMode = op.mode;
- op.mode = mode;
+ if (op.getMode() != mode) {
+ previousMode = op.getMode();
+ op.setMode(mode);
+
if (uidState != null) {
uidState.evalForegroundOps(mOpModeWatchers);
}
@@ -2976,17 +2997,14 @@
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- SparseIntArray opModes = uidState.opModes;
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
final int code = opModes.keyAt(j);
if (AppOpsManager.opAllowsReset(code)) {
int previousMode = opModes.valueAt(j);
- opModes.removeAt(j);
- if (opModes.size() <= 0) {
- uidState.opModes = null;
- }
+ uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -3028,9 +3046,9 @@
continue;
}
if (AppOpsManager.opAllowsReset(curOp.op)
- && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
- int previousMode = curOp.mode;
- curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
+ && curOp.getMode() != AppOpsManager.opToDefaultMode(curOp.op)) {
+ int previousMode = curOp.getMode();
+ curOp.setMode(AppOpsManager.opToDefaultMode(curOp.op));
changed = true;
uidChanged = true;
final int uid = curOp.uidState.uid;
@@ -3049,9 +3067,11 @@
}
if (pkgOps.size() == 0) {
it.remove();
+ mAppOpsServiceInterface.removePackage(packageName);
}
}
if (uidState.isDefault()) {
+ uidState.clear();
mUidStates.remove(uidState.uid);
}
if (uidChanged) {
@@ -3267,16 +3287,16 @@
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
- if (uidState != null && uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0) {
- final int rawMode = uidState.opModes.get(code);
+ if (uidState != null
+ && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+ final int rawMode = uidState.getUidMode(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return raw ? op.mode : op.evalMode();
+ return raw ? op.getMode() : op.evalMode();
}
}
@@ -3502,8 +3522,8 @@
}
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -4018,8 +4038,8 @@
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -4516,9 +4536,8 @@
continue;
}
- if (uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0
- && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) {
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
+ && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true, null));
@@ -4536,7 +4555,7 @@
if (op == null) {
continue;
}
- if (op.mode == AppOpsManager.MODE_FOREGROUND) {
+ if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, callback, code, uidState.uid,
@@ -4811,14 +4830,16 @@
return ops;
}
- private void scheduleWriteLocked() {
+ @Override
+ public void scheduleWriteLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
}
}
- private void scheduleFastWriteLocked() {
+ @Override
+ public void scheduleFastWriteLocked() {
if (!mFastWriteScheduled) {
mWriteScheduled = true;
mFastWriteScheduled = true;
@@ -4929,6 +4950,7 @@
}
boolean success = false;
mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
try {
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
@@ -4977,6 +4999,7 @@
} finally {
if (!success) {
mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
}
try {
stream.close();
@@ -4996,11 +5019,12 @@
if (uidState == null) {
continue;
}
- if (uidState.opModes != null) {
- final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null) {
+ final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
if (idx >= 0) {
- uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uidState.opModes.valueAt(idx));
+ uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ opModes.valueAt(idx));
}
}
if (uidState.pkgOps == null) {
@@ -5011,10 +5035,10 @@
Ops ops = uidState.pkgOps.valueAt(j);
if (ops != null) {
final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
- if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
+ if (op != null && op.getMode() != AppOpsManager.opToDefaultMode(op.op)) {
final Op copy = new Op(op.uidState, op.packageName,
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.uid);
- copy.mode = op.mode;
+ copy.setMode(op.getMode());
ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
changed = true;
}
@@ -5142,7 +5166,7 @@
Op op = new Op(uidState, pkgName, opCode, uidState.uid);
final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
- op.mode = mode;
+ op.setMode(mode);
int outerDepth = parser.getDepth();
int type;
@@ -5199,16 +5223,9 @@
UidState uidState = mUidStates.valueAt(uidStateNum);
int uid = mUidStates.keyAt(uidStateNum);
- SparseIntArray opModes = uidState.opModes;
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null && opModes.size() > 0) {
- uidStatesClone.put(uid, new SparseIntArray(opModes.size()));
-
- final int opCount = opModes.size();
- for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
- uidStatesClone.get(uid).put(
- opModes.keyAt(opCountNum),
- opModes.valueAt(opCountNum));
- }
+ uidStatesClone.put(uid, opModes);
}
}
}
@@ -6314,15 +6331,15 @@
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
- final SparseIntArray opModes = uidState.opModes;
+ final SparseIntArray opModes = uidState.getNonDefaultUidModes();
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
continue;
}
if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
- boolean hasOp = dumpOp < 0 || (uidState.opModes != null
- && uidState.opModes.indexOfKey(dumpOp) >= 0);
+ boolean hasOp = dumpOp < 0 || (opModes != null
+ && opModes.indexOfKey(dumpOp) >= 0);
boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
boolean hasMode = dumpMode < 0;
if (!hasMode && opModes != null) {
@@ -6342,7 +6359,7 @@
}
if (!hasMode) {
for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
- if (ops.valueAt(opi).mode == dumpMode) {
+ if (ops.valueAt(opi).getMode() == dumpMode) {
hasMode = true;
}
}
@@ -6437,7 +6454,7 @@
if (dumpOp >= 0 && dumpOp != opCode) {
continue;
}
- if (dumpMode >= 0 && dumpMode != op.mode) {
+ if (dumpMode >= 0 && dumpMode != op.getMode()) {
continue;
}
if (!printedPackage) {
@@ -6445,14 +6462,14 @@
printedPackage = true;
}
pw.print(" "); pw.print(AppOpsManager.opToName(opCode));
- pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode));
+ pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode()));
final int switchOp = AppOpsManager.opToSwitch(opCode);
if (switchOp != opCode) {
pw.print(" / switch ");
pw.print(AppOpsManager.opToName(switchOp));
final Op switchObj = ops.get(switchOp);
- int mode = switchObj != null ? switchObj.mode
- : AppOpsManager.opToDefaultMode(switchOp);
+ int mode = switchObj == null
+ ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
@@ -6696,7 +6713,7 @@
for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
Ops ops = uidState.pkgOps.valueAt(pkgNum);
Op op = ops != null ? ops.get(code) : null;
- if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) {
+ if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
continue;
}
int numAttrTags = op.mAttributions.size();
@@ -6817,6 +6834,7 @@
return;
}
Ops removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName);
if (removedOps != null) {
scheduleFastWriteLocked();
}
@@ -7136,10 +7154,12 @@
return false;
}
+ @GuardedBy("this")
private void removeUidsForUserLocked(int userHandle) {
for (int i = mUidStates.size() - 1; i >= 0; --i) {
final int uid = mUidStates.keyAt(i);
if (UserHandle.getUserId(uid) == userHandle) {
+ mUidStates.valueAt(i).clear();
mUidStates.removeAt(i);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
new file mode 100644
index 0000000..cd5ea12
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -0,0 +1,98 @@
+/*
+ * 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.appop;
+import android.annotation.NonNull;
+import android.app.AppOpsManager.Mode;
+import android.util.SparseIntArray;
+/**
+ * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
+ * In the future this interface will also include mode callbacks and op restrictions.
+ */
+public interface AppOpsServiceInterface {
+ /**
+ * Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
+ * Returns an empty SparseIntArray if nothing is set.
+ * @param uid for which we need the app-ops and their modes.
+ */
+ SparseIntArray getNonDefaultUidModes(int uid);
+
+ /**
+ * Returns the app-op mode for a particular app-op of a uid.
+ * Returns default op mode if the op mode for particular uid and op is not set.
+ * @param uid user id for which we need the mode.
+ * @param op app-op for which we need the mode.
+ * @return mode of the app-op.
+ */
+ int getUidMode(int uid, int op);
+
+ /**
+ * Set the app-op mode for a particular uid and op.
+ * The mode is not set if the mode is the same as the default mode for the op.
+ * @param uid user id for which we want to set the mode.
+ * @param op app-op for which we want to set the mode.
+ * @param mode mode for the app-op.
+ * @return true if op mode is changed.
+ */
+ boolean setUidMode(int uid, int op, @Mode int mode);
+
+ /**
+ * Gets the app-op mode for a particular package.
+ * Returns default op mode if the op mode for the particular package is not set.
+ * @param packageName package name for which we need the op mode.
+ * @param op app-op for which we need the mode.
+ * @return the mode of the app-op.
+ */
+ int getPackageMode(@NonNull String packageName, int op);
+
+ /**
+ * Sets the app-op mode for a particular package.
+ * @param packageName package name for which we need to set the op mode.
+ * @param op app-op for which we need to set the mode.
+ * @param mode the mode of the app-op.
+ */
+ void setPackageMode(@NonNull String packageName, int op, @Mode int mode);
+
+ /**
+ * Stop tracking any app-op modes for a package.
+ * @param packageName Name of the package for which we want to remove all mode tracking.
+ */
+ boolean removePackage(@NonNull String packageName);
+
+ /**
+ * Stop tracking any app-op modes for this uid.
+ * @param uid user id for which we want to remove all tracking.
+ */
+ void removeUid(int uid);
+
+ /**
+ * Returns true if all uid modes for this uid are
+ * in default state.
+ * @param uid user id
+ */
+ boolean areUidModesDefault(int uid);
+
+ /**
+ * Returns true if all package modes for this package name are
+ * in default state.
+ * @param packageName package name.
+ */
+ boolean arePackageModesDefault(String packageName);
+
+ /**
+ * Stop tracking app-op modes for all uid and packages.
+ */
+ void clearAllModes();
+}
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
new file mode 100644
index 0000000..c27c0d3
--- /dev/null
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -0,0 +1,198 @@
+/*
+ * 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.appop;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.Mode;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Legacy implementation for App-ops service's app-op mode (uid and package) storage and access.
+ * In the future this class will also include mode callbacks and op restrictions.
+ */
+public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {
+
+ // Should be the same object that the AppOpsService is using for locking.
+ final Object mLock;
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final SparseArray<SparseIntArray> mUidModes = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();
+
+ final PersistenceScheduler mPersistenceScheduler;
+
+
+ LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
+ @NonNull Object lock) {
+ this.mPersistenceScheduler = persistenceScheduler;
+ this.mLock = lock;
+ }
+
+ @Override
+ public SparseIntArray getNonDefaultUidModes(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ return new SparseIntArray();
+ }
+ return opModes.clone();
+ }
+ }
+
+ @Override
+ public int getUidMode(int uid, int op) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ return AppOpsManager.opToDefaultMode(op);
+ }
+ return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+ }
+ }
+
+ @Override
+ public boolean setUidMode(int uid, int op, int mode) {
+ final int defaultMode = AppOpsManager.opToDefaultMode(op);
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ if (mode != defaultMode) {
+ opModes = new SparseIntArray();
+ mUidModes.put(uid, opModes);
+ opModes.put(op, mode);
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ } else {
+ if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+ return false;
+ }
+ if (mode == defaultMode) {
+ opModes.delete(op);
+ if (opModes.size() <= 0) {
+ opModes = null;
+ mUidModes.delete(uid);
+ }
+ } else {
+ opModes.put(op, mode);
+ }
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int getPackageMode(String packageName, int op) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null);
+ if (opModes == null) {
+ return AppOpsManager.opToDefaultMode(op);
+ }
+ return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+ }
+ }
+
+ @Override
+ public void setPackageMode(String packageName, int op, @Mode int mode) {
+ final int defaultMode = AppOpsManager.opToDefaultMode(op);
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.get(packageName);
+ if (opModes == null) {
+ if (mode != defaultMode) {
+ opModes = new SparseIntArray();
+ mPackageModes.put(packageName, opModes);
+ opModes.put(op, mode);
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ } else {
+ if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+ return;
+ }
+ if (mode == defaultMode) {
+ opModes.delete(op);
+ if (opModes.size() <= 0) {
+ opModes = null;
+ mPackageModes.remove(packageName);
+ }
+ } else {
+ opModes.put(op, mode);
+ }
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ }
+ }
+
+ @Override
+ public void removeUid(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid);
+ if (opModes == null) {
+ return;
+ }
+ mUidModes.remove(uid);
+ mPersistenceScheduler.scheduleFastWriteLocked();
+ }
+ }
+
+
+ @Override
+ public boolean areUidModesDefault(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid);
+ return (opModes == null || opModes.size() <= 0);
+ }
+ }
+
+ @Override
+ public boolean arePackageModesDefault(String packageMode) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.get(packageMode);
+ return (opModes == null || opModes.size() <= 0);
+ }
+ }
+
+ @Override
+ public boolean removePackage(String packageName) {
+ synchronized (mLock) {
+ SparseIntArray ops = mPackageModes.remove(packageName);
+ if (ops != null) {
+ mPersistenceScheduler.scheduleFastWriteLocked();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void clearAllModes() {
+ synchronized (mLock) {
+ mUidModes.clear();
+ mPackageModes.clear();
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/appop/PersistenceScheduler.java b/services/core/java/com/android/server/appop/PersistenceScheduler.java
new file mode 100644
index 0000000..e50b658
--- /dev/null
+++ b/services/core/java/com/android/server/appop/PersistenceScheduler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.appop;
+
+/**
+ * Interface that allows callers to persist AppOpsService's internal state
+ * to disk.
+ */
+public interface PersistenceScheduler {
+
+ /**
+ * Schedules disk writes for appOpsService and it's internal states.
+ */
+ void scheduleWriteLocked();
+
+ /**
+ * Schedules fast disk writes for appOpsService and it's internal states.
+ */
+ void scheduleFastWriteLocked();
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 298ee97..5a20db3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -369,6 +369,7 @@
private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
private static final int MSG_ROTATION_UPDATE = 48;
private static final int MSG_FOLD_UPDATE = 49;
+ private static final int MSG_RESET_SPATIALIZER = 50;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -376,6 +377,7 @@
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
private static final int MSG_INIT_SPATIALIZER = 102;
+
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -8305,6 +8307,10 @@
onPersistSpatialAudioDeviceSettings();
break;
+ case MSG_RESET_SPATIALIZER:
+ mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -9283,6 +9289,16 @@
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
}
+ /**
+ * post a message to schedule a reset of the spatializer state
+ */
+ void postResetSpatializer() {
+ sendMsg(mAudioHandler,
+ MSG_RESET_SPATIALIZER,
+ SENDMSG_REPLACE,
+ /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
+ }
+
void onInitSpatializer() {
final String settings = mSettings.getSecureStringForUser(mContentResolver,
Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index d9037fe..66a4527 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,6 +53,8 @@
private static final String TAG = "AS.BtHelper";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private final @NonNull AudioDeviceBroker mDeviceBroker;
BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -911,6 +913,8 @@
return "ENCODING_APTX_HD";
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
return "ENCODING_LDAC";
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return "ENCODING_OPUS";
default:
return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 9ff72d3..4559c56 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,10 +17,12 @@
package com.android.server.audio;
import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_CLIENT_VOLUME;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_MASTER;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_PLAYBACK_RESTRICTED;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_MUTED;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_VOLUME;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_VOLUME_SHAPER;
import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED;
@@ -1145,6 +1147,10 @@
(mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
builder.append(" muteFromPlaybackRestricted:").append(
(mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
+ builder.append(" muteFromClientVolume:").append(
+ (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
+ builder.append(" muteFromVolumeShaper:").append(
+ (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
return builder.toString();
default:
return builder.toString();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1def72b..e27fb11 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -389,10 +389,10 @@
try {
mSpat.setLevel(level);
} catch (RemoteException e) {
- Log.e(TAG, "Can't set spatializer level", e);
- mState = STATE_NOT_SUPPORTED;
- mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- enabled = false;
+ Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e);
+ // try to recover by resetting the native spatializer state
+ postReset();
+ return;
}
}
@@ -404,6 +404,10 @@
}
}
+ private void postReset() {
+ mAudioService.postResetSpatializer();
+ }
+
//------------------------------------------------------
// spatializer callback from native
private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
@@ -751,33 +755,29 @@
if (enabled) {
throw (new IllegalStateException("Can't enable when uninitialized"));
}
- return;
+ break;
case STATE_NOT_SUPPORTED:
if (enabled) {
Log.e(TAG, "Can't enable when unsupported");
}
- return;
+ break;
case STATE_DISABLED_UNAVAILABLE:
case STATE_DISABLED_AVAILABLE:
if (enabled) {
createSpat();
onRoutingUpdated();
- break;
- } else {
- // already in disabled state
- return;
- }
+ // onRoutingUpdated() can update the "enabled" state based on context
+ // and will call setDispatchFeatureEnabledState().
+ } // else { nothing to do as already disabled }
+ break;
case STATE_ENABLED_UNAVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (!enabled) {
releaseSpat();
- break;
- } else {
- // already in enabled state
- return;
- }
+ setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt");
+ } // else { nothing to do as already enabled }
+ break;
}
- setDispatchFeatureEnabledState(enabled, "setSpatializerEnabledInt");
}
synchronized int getCapableImmersiveAudioLevel() {
@@ -1161,8 +1161,11 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer when calling " + funcName));
+ // try to recover by resetting the native spatializer state
+ Log.e(TAG, "checkSpatForHeadTracking(): "
+ + "native spatializer should not be null in state: " + mState);
+ postReset();
+ return false;
}
break;
}
@@ -1252,8 +1255,8 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for setParameter for key:" + key));
+ Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
@@ -1276,8 +1279,8 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for getParameter for key:" + key));
+ Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 351a1e9..23183f9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -26,11 +26,14 @@
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.server.SystemService;
import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -46,7 +49,7 @@
private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2;
private final Object mLock = new Object();
- private List<RadioManager.ModuleProperties> mV1Modules = null;
+ private final List<RadioManager.ModuleProperties> mV1Modules;
public BroadcastRadioService(Context context) {
super(context);
@@ -115,5 +118,21 @@
return mHal2.addAnnouncementListener(enabledTypes, listener);
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
+ radioPw.printf("BroadcastRadioService\n");
+ radioPw.increaseIndent();
+ radioPw.printf("HAL1: %s\n", mHal1);
+ radioPw.increaseIndent();
+ radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+ radioPw.decreaseIndent();
+ radioPw.printf("HAL2:\n");
+ radioPw.increaseIndent();
+ mHal2.dumpInfo(radioPw);
+ radioPw.decreaseIndent();
+ radioPw.decreaseIndent();
+ }
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 534e828..5605737 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -29,6 +29,7 @@
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -195,4 +196,30 @@
}
return aggregator;
}
+
+ /**
+ * Dump state of broadcastradio service for HIDL HAL 2.0.
+ *
+ * @param pw The file to which BroadcastRadioService state is dumped.
+ */
+ public void dumpInfo(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.printf("Next module id available: %d\n", mNextModuleId);
+ pw.printf("ServiceName to module id map:\n");
+ pw.increaseIndent();
+ for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
+ pw.printf("Service name: %s, module id: %d\n", entry.getKey(), entry.getValue());
+ }
+ pw.decreaseIndent();
+ pw.printf("Radio modules:\n");
+ pw.increaseIndent();
+ for (Map.Entry<Integer, RadioModule> moduleEntry : mModules.entrySet()) {
+ pw.printf("Module id=%d:\n", moduleEntry.getKey());
+ pw.increaseIndent();
+ moduleEntry.getValue().dumpInfo(pw);
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index aeaa678..852aa66 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -38,6 +38,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import android.util.Slog;
@@ -424,4 +425,30 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("RadioModule\n");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("BroadcastRadioService: %s\n", mService);
+ pw.printf("Properties: %s\n", mProperties);
+ pw.printf("HIDL2.0 HAL TunerSession: %s\n", mHalTunerSession);
+ pw.printf("Is antenna connected? ");
+ if (mAntennaConnected == null) {
+ pw.printf("null\n");
+ } else {
+ pw.printf("%s\n", mAntennaConnected ? "Yes" : "No");
+ }
+ pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
+ pw.printf("AIDL TunerSessions:\n");
+ pw.increaseIndent();
+ for (TunerSession aidlTunerSession : mAidlTunerSessions) {
+ aidlTunerSession.dumpInfo(pw);
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index c13216b..41f753c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,7 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
@@ -339,4 +340,17 @@
() -> mHwSession.getParameters(Convert.listToArrayList(keys))));
}
}
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("TunerSession\n");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("HIDL HAL Session: %s\n", mHwSession);
+ pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
+ pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Config: %s\n", mDummyConfig);
+ }
+ pw.decreaseIndent();
+ }
}
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 f2b4d42..230bfc5 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.companion.virtual;
+import android.annotation.NonNull;
import android.companion.virtual.IVirtualDevice;
/**
@@ -24,16 +25,41 @@
*/
public abstract class VirtualDeviceManagerInternal {
+ /** Interface to listen to the creation and destruction of virtual displays. */
+ public interface VirtualDisplayListener {
+ /** Notifies that a virtual display was created. */
+ void onVirtualDisplayCreated(int displayId);
+
+ /** Notifies that a virtual display was removed. */
+ void onVirtualDisplayRemoved(int displayId);
+ }
+
+ /** Register a listener for the creation and destruction of virtual displays. */
+ public abstract void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+ /** Unregister a listener for the creation and destruction of virtual displays. */
+ public abstract void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+
/**
* Validate the virtual device.
*/
public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
/**
- * Notify a virtual display is removed.
+ * Notifies that a virtual display is created.
+ *
+ * @param displayId The display id of the created virtual display.
+ */
+ public abstract void onVirtualDisplayCreated(int displayId);
+
+ /**
+ * Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
- * @param displayId The display id of the removed virtual display.
+ * @param displayId The display id of the removed virtual display.
*/
public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b343480..2dd3864 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2583,8 +2583,8 @@
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
final DisplayPowerController displayPowerController = new DisplayPowerController(
- mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display));
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c01a228..e71e29b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,6 +48,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.FloatProperty;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableFloat;
@@ -58,6 +59,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
@@ -97,7 +99,7 @@
* blocker as long as the display is not ready. So most of the work done here
* does not need to worry about holding a suspend blocker unless it happens
* independently of the display ready signal.
- *
+ *
* For debugging, you can make the color fade and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
@@ -160,7 +162,7 @@
private static final int RINGBUFFER_MAX = 100;
- private final String TAG;
+ private final String mTag;
private final Object mLock = new Object();
@@ -256,6 +258,9 @@
// to reach the final state.
private final boolean mBrightnessBucketsInDozeConfig;
+ private final Clock mClock;
+ private final Injector mInjector;
+
// Maximum time a ramp animation can take.
private long mBrightnessRampIncreaseMaxTimeMillis;
private long mBrightnessRampDecreaseMaxTimeMillis;
@@ -495,20 +500,22 @@
/**
* Creates the display power controller.
*/
- public DisplayPowerController(Context context,
+ DisplayPowerController(Context context, Injector injector,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable) {
+
+ mInjector = injector != null ? injector : new Injector();
+ mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
- final String displayIdStr = "[" + mDisplayId + "]";
- TAG = "DisplayPowerController" + displayIdStr;
- mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business";
- mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed";
- mSuspendBlockerIdProxPositive = displayIdStr + "prox positive";
- mSuspendBlockerIdProxNegative = displayIdStr + "prox negative";
- mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce";
+ mTag = "DisplayPowerController[" + mDisplayId + "]";
+ mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+ mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+ mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+ mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+ mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -593,7 +600,7 @@
displayWhiteBalanceSettings.setCallbacks(this);
displayWhiteBalanceController.setCallbacks(this);
} catch (Exception e) {
- Slog.e(TAG, "failed to set up display white-balance: " + e);
+ Slog.e(mTag, "failed to set up display white-balance: " + e);
}
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
@@ -660,7 +667,7 @@
&& mInteractiveModeBrightnessMapper == null)
|| (mAutomaticBrightnessController.isInIdleMode()
&& mIdleModeBrightnessMapper == null)) {
- Log.w(TAG, "No brightness mapping available to recalculate splines for this mode");
+ Log.w(mTag, "No brightness mapping available to recalculate splines for this mode");
return;
}
@@ -681,7 +688,8 @@
/**
* Get the {@link BrightnessChangeEvent}s for the specified user.
- * @param userId userId to fetch data for
+ *
+ * @param userId userId to fetch data for
* @param includePackage if false will null out the package name in events
*/
@Nullable
@@ -723,10 +731,11 @@
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
- * @param request The requested power state.
+ * @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
- * negative proximity before turning the screen back on, assuming the screen
- * was turned off by the proximity sensor.
+ * negative proximity before turning the screen back on,
+ * assuming the screen
+ * was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
@@ -735,7 +744,7 @@
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
if (DEBUG) {
- Slog.d(TAG, "requestPowerState: "
+ Slog.d(mTag, "requestPowerState: "
+ request + ", waitForNegativeProximity=" + waitForNegativeProximity);
}
@@ -788,7 +797,7 @@
public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
+ mLogicalDisplay.getDisplayIdLocked());
return;
}
@@ -834,7 +843,7 @@
synchronized (mLock) {
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setEnabled(false);
@@ -888,12 +897,12 @@
if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
}
private void initialize(int displayState) {
- mPowerState = new DisplayPowerState(mBlanker,
+ mPowerState = mInjector.getDisplayPowerState(mBlanker,
mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
if (mColorFadeEnabled) {
@@ -908,7 +917,7 @@
mColorFadeOffAnimator.addListener(mAnimatorListener);
}
- mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
+ mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setAnimationTimeLimits(
@@ -1002,7 +1011,7 @@
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
} else if (initialLightSensorRate > lightSensorRate) {
- Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
+ Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
+ initialLightSensorRate + ") to be less than or equal to "
+ "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
}
@@ -1048,7 +1057,7 @@
if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
mNitsRange = mDisplayDeviceConfig.getNits();
} else {
- Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+ Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
}
@@ -1077,13 +1086,16 @@
@Override
public void onAnimationStart(Animator animation) {
}
+
@Override
public void onAnimationEnd(Animator animation) {
sendUpdatePowerState();
}
+
@Override
public void onAnimationRepeat(Animator animation) {
}
+
@Override
public void onAnimationCancel(Animator animation) {
}
@@ -1124,8 +1136,8 @@
mOnProximityNegativeMessages = 0;
final float brightness = mPowerState != null
- ? mPowerState.getScreenBrightness()
- : PowerManager.BRIGHTNESS_MIN;
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
reportStats(brightness);
if (mPowerState != null) {
@@ -1203,7 +1215,7 @@
state = Display.STATE_ON;
break;
}
- assert(state != Display.STATE_UNKNOWN);
+ assert (state != Display.STATE_UNKNOWN);
// Apply the proximity sensor.
if (mProximitySensor != null) {
@@ -1290,16 +1302,16 @@
final boolean autoBrightnessEnabledInDoze =
mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
- && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
- && Float.isNaN(brightnessState)
- && mAutomaticBrightnessController != null;
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && Float.isNaN(brightnessState)
+ && mAutomaticBrightnessController != null;
final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
- && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
: autoBrightnessDisabledDueToDisplayOff
- ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
- : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
@@ -1589,9 +1601,9 @@
// AND if we are not in idle screen brightness mode.
if (!brightnessIsTemporary
&& (mAutomaticBrightnessController != null
- && !mAutomaticBrightnessController.isInIdleMode())) {
+ && !mAutomaticBrightnessController.isInIdleMode())) {
if (userInitiatedChange && (mAutomaticBrightnessController == null
- || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
// If we don't have a valid lux reading we can't report a valid
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
@@ -1614,13 +1626,13 @@
// Log any changes to what is currently driving the brightness setting.
if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
- Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '"
+ Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
+ mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ "', previous reason: '" + mBrightnessReason + "'.");
mBrightnessReason.set(mBrightnessReasonTemp);
} else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
&& userSetBrightnessChanged) {
- Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
+ Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
}
@@ -1638,7 +1650,7 @@
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
- && mLastBrightnessEvent.getReason().getReason()
+ && mLastBrightnessEvent.getReason().getReason()
== BrightnessReason.REASON_TEMPORARY;
if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
|| brightnessAdjustmentFlags != 0) {
@@ -1650,7 +1662,7 @@
newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
? BrightnessEvent.FLAG_USER_SET : 0));
- Slog.i(TAG, newEvent.toString(/* includeTime= */ false));
+ Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
if (mBrightnessEventRingBuffer != null) {
mBrightnessEventRingBuffer.append(newEvent);
@@ -1671,9 +1683,9 @@
// Note that we do not wait for the brightness ramp animation to complete before
// reporting the display is ready because we only need to ensure the screen is in the
// right power state even as it continues to converge on the desired brightness.
- final boolean ready = mPendingScreenOnUnblocker == null &&
- (!mColorFadeEnabled ||
- (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
+ final boolean ready = mPendingScreenOnUnblocker == null
+ && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
+ && !mColorFadeOffAnimator.isStarted()))
&& mPowerState.waitUntilClean(mCleanListener);
final boolean finished = ready
&& !mScreenBrightnessRampAnimator.isAnimating();
@@ -1688,7 +1700,7 @@
// Grab a wake lock if we have unfinished business.
if (!finished && !mUnfinishedBusiness) {
if (DEBUG) {
- Slog.d(TAG, "Unfinished business...");
+ Slog.d(mTag, "Unfinished business...");
}
mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
mUnfinishedBusiness = true;
@@ -1702,7 +1714,7 @@
mDisplayReadyLocked = true;
if (DEBUG) {
- Slog.d(TAG, "Display ready!");
+ Slog.d(mTag, "Display ready!");
}
}
}
@@ -1712,7 +1724,7 @@
// Release the wake lock when we have no unfinished business.
if (finished && mUnfinishedBusiness) {
if (DEBUG) {
- Slog.d(TAG, "Finished business...");
+ Slog.d(mTag, "Finished business...");
}
mUnfinishedBusiness = false;
mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
@@ -1782,26 +1794,26 @@
boolean changed = false;
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
- brightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
- adjustedBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
- minBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ minBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
- maxBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ maxBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
- mHbmController.getHighBrightnessMode());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
- mHbmController.getTransitionPoint());
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
- mBrightnessThrottler.getBrightnessMaxReason());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
return changed;
}
@@ -1856,7 +1868,7 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
mPendingScreenOnUnblocker = new ScreenOnUnblocker();
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
- Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
+ Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
}
}
@@ -1864,7 +1876,7 @@
if (mPendingScreenOnUnblocker != null) {
mPendingScreenOnUnblocker = null;
long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
- Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
+ Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
}
}
@@ -1874,7 +1886,7 @@
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
mPendingScreenOffUnblocker = new ScreenOffUnblocker();
mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
- Slog.i(TAG, "Blocking screen off");
+ Slog.i(mTag, "Blocking screen off");
}
}
@@ -1882,7 +1894,7 @@
if (mPendingScreenOffUnblocker != null) {
mPendingScreenOffUnblocker = null;
long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
- Slog.i(TAG, "Unblocked screen off after " + delay + " ms");
+ Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
}
}
@@ -1943,7 +1955,7 @@
}
if (!isOff
&& (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
- || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
if (mPowerState.getColorFadeLevel() == 0.0f) {
blockScreenOn();
@@ -2008,7 +2020,7 @@
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
if (DEBUG) {
- Slog.d(TAG, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ ", rate=" + rate);
}
if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
@@ -2021,8 +2033,8 @@
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
// If there is already an animation in progress, don't interfere with it.
- if (mColorFadeEnabled &&
- (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
+ if (mColorFadeEnabled
+ && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
if (target != Display.STATE_ON) {
return;
}
@@ -2069,9 +2081,8 @@
if (mPowerState.getColorFadeLevel() == 1.0f) {
mPowerState.dismissColorFade();
} else if (mPowerState.prepareColorFade(mContext,
- mColorFadeFadesConfig ?
- ColorFade.MODE_FADE :
- ColorFade.MODE_WARM_UP)) {
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
@@ -2173,8 +2184,8 @@
mPowerState.dismissColorFadeResources();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
- mColorFadeFadesConfig ?
- ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
&& mPowerState.getScreenState() != Display.STATE_OFF) {
// Perform the screen off animation.
mColorFadeOffAnimator.start();
@@ -2245,12 +2256,12 @@
if (mProximitySensorEnabled
&& mPendingProximity != PROXIMITY_UNKNOWN
&& mPendingProximityDebounceTime >= 0) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
if (mPendingProximityDebounceTime <= now) {
if (mProximity != mPendingProximity) {
// if the status of the sensor changed, stop ignoring.
mIgnoreProximityUntilChanged = false;
- Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]");
+ Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
}
// Sensor reading accepted. Apply the change then release the wake lock.
mProximity = mPendingProximity;
@@ -2435,7 +2446,7 @@
&& mProximity == PROXIMITY_POSITIVE) {
// Only ignore if it is still reporting positive (near)
mIgnoreProximityUntilChanged = true;
- Slog.i(TAG, "Ignoring proximity");
+ Slog.i(mTag, "Ignoring proximity");
updatePowerState();
}
}
@@ -2505,25 +2516,25 @@
pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
- pw.println(" mAllowAutoBrightnessWhileDozingConfig=" +
- mAllowAutoBrightnessWhileDozingConfig);
+ pw.println(" mAllowAutoBrightnessWhileDozingConfig="
+ + mAllowAutoBrightnessWhileDozingConfig);
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
synchronized (mCachedBrightnessInfo) {
- pw.println(" mCachedBrightnessInfo.brightness=" +
- mCachedBrightnessInfo.brightness.value);
- pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
- mCachedBrightnessInfo.adjustedBrightness.value);
- pw.println(" mCachedBrightnessInfo.brightnessMin=" +
- mCachedBrightnessInfo.brightnessMin.value);
- pw.println(" mCachedBrightnessInfo.brightnessMax=" +
- mCachedBrightnessInfo.brightnessMax.value);
+ pw.println(" mCachedBrightnessInfo.brightness="
+ + mCachedBrightnessInfo.brightness.value);
+ pw.println(" mCachedBrightnessInfo.adjustedBrightness="
+ + mCachedBrightnessInfo.adjustedBrightness.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMin="
+ + mCachedBrightnessInfo.brightnessMin.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMax="
+ + mCachedBrightnessInfo.brightnessMax.value);
pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
- pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
- mCachedBrightnessInfo.hbmTransitionPoint.value);
- pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" +
- mCachedBrightnessInfo.brightnessMaxReason .value);
+ pw.println(" mCachedBrightnessInfo.hbmTransitionPoint="
+ + mCachedBrightnessInfo.hbmTransitionPoint.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMaxReason ="
+ + mCachedBrightnessInfo.brightnessMaxReason.value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2706,7 +2717,7 @@
}
float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
- synchronized(mCachedBrightnessInfo) {
+ synchronized (mCachedBrightnessInfo) {
if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
return;
}
@@ -2740,10 +2751,8 @@
}
}
-
-
private final class DisplayControllerHandler extends Handler {
- public DisplayControllerHandler(Looper looper) {
+ DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@@ -2806,7 +2815,7 @@
break;
case MSG_BRIGHTNESS_RAMP_DONE:
- if (mPowerState != null) {
+ if (mPowerState != null) {
final float brightness = mPowerState.getScreenBrightness();
reportStats(brightness);
}
@@ -2823,7 +2832,7 @@
@Override
public void onSensorChanged(SensorEvent event) {
if (mProximitySensorEnabled) {
- final long time = SystemClock.uptimeMillis();
+ final long time = mClock.uptimeMillis();
final float distance = event.values[0];
boolean positive = distance >= 0.0f && distance < mProximityThreshold;
handleProximitySensorEvent(time, positive);
@@ -2838,7 +2847,7 @@
private final class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
+ SettingsObserver(Handler handler) {
super(handler);
}
@@ -2892,19 +2901,68 @@
}
}
+ @VisibleForTesting
+ String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+ return "[" + displayId + "]unfinished business";
+ }
+
+ String getSuspendBlockerOnStateChangedId(int displayId) {
+ return "[" + displayId + "]on state changed";
+ }
+
+ String getSuspendBlockerProxPositiveId(int displayId) {
+ return "[" + displayId + "]prox positive";
+ }
+
+ String getSuspendBlockerProxNegativeId(int displayId) {
+ return "[" + displayId + "]prox negative";
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerProxDebounceId(int displayId) {
+ return "[" + displayId + "]prox debounce";
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+ }
+
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return new DualRampAnimator(dps, firstProperty, secondProperty);
+ }
+ }
+
static class CachedBrightnessInfo {
public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat adjustedBrightness =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMin =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMax =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
public MutableFloat hbmTransitionPoint =
- new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
public MutableInt brightnessMaxReason =
- new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
public boolean checkAndSetFloat(MutableFloat mf, float f) {
if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 690ec3f..52b92c4 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -29,7 +29,6 @@
class RampAnimator<T> {
private final T mObject;
private final FloatProperty<T> mProperty;
- private final Choreographer mChoreographer;
private float mCurrentValue;
private float mTargetValue;
@@ -43,18 +42,16 @@
private boolean mFirstTime = true;
- private Listener mListener;
RampAnimator(T object, FloatProperty<T> property) {
mObject = object;
mProperty = property;
- mChoreographer = Choreographer.getInstance();
}
/**
* Sets the maximum time that a brightness animation can take.
*/
- public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
+ void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
long animationRampDecreaseMaxTimeMillis) {
mAnimationIncreaseMaxTimeSecs = (animationRampIncreaseMaxTimeMillis > 0)
? (animationRampIncreaseMaxTimeMillis / 1000.0f) : 0.0f;
@@ -63,7 +60,7 @@
}
/**
- * Starts animating towards the specified value.
+ * Sets the animation target and the rate of this ramp animator.
*
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
@@ -72,7 +69,7 @@
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if the target differs from the previous target.
*/
- public boolean animateTo(float targetLinear, float rate) {
+ boolean setAnimationTarget(float targetLinear, float rate) {
// Convert the target from the linear into the HLG space.
final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
@@ -84,13 +81,7 @@
mTargetValue = target;
mCurrentValue = target;
setPropertyValue(target);
- if (mAnimating) {
- mAnimating = false;
- cancelAnimationCallback();
- }
- if (mListener != null) {
- mListener.onAnimationEnd();
- }
+ mAnimating = false;
return true;
}
return false;
@@ -127,7 +118,6 @@
mAnimating = true;
mAnimatedValue = mCurrentValue;
mLastFrameTimeNanos = System.nanoTime();
- postAnimationCallback();
}
return changed;
@@ -136,18 +126,11 @@
/**
* Returns true if the animation is running.
*/
- public boolean isAnimating() {
+ boolean isAnimating() {
return mAnimating;
}
/**
- * Sets a listener to watch for animation events.
- */
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
- /**
* Sets the brightness property by converting the given value from HLG space
* into linear space.
*/
@@ -156,78 +139,53 @@
mProperty.setValue(mObject, linearVal);
}
- private void postAnimationCallback() {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
- }
+ void performNextAnimationStep(long frameTimeNanos) {
+ final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) * 0.000000001f;
+ mLastFrameTimeNanos = frameTimeNanos;
- private void cancelAnimationCallback() {
- mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
- }
-
- private final Runnable mAnimationCallback = new Runnable() {
- @Override // Choreographer callback
- public void run() {
- final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
- final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
- * 0.000000001f;
- mLastFrameTimeNanos = frameTimeNanos;
-
- // Advance the animated value towards the target at the specified rate
- // and clamp to the target. This gives us the new current value but
- // we keep the animated value around to allow for fractional increments
- // towards the target.
- final float scale = ValueAnimator.getDurationScale();
- if (scale == 0) {
- // Animation off.
- mAnimatedValue = mTargetValue;
+ // Advance the animated value towards the target at the specified rate
+ // and clamp to the target. This gives us the new current value but
+ // we keep the animated value around to allow for fractional increments
+ // towards the target.
+ final float scale = ValueAnimator.getDurationScale();
+ if (scale == 0) {
+ // Animation off.
+ mAnimatedValue = mTargetValue;
+ } else {
+ final float amount = timeDelta * mRate / scale;
+ if (mTargetValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
} else {
- final float amount = timeDelta * mRate / scale;
- if (mTargetValue > mCurrentValue) {
- mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
- } else {
- mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
- }
- }
- final float oldCurrentValue = mCurrentValue;
- mCurrentValue = mAnimatedValue;
- if (oldCurrentValue != mCurrentValue) {
- setPropertyValue(mCurrentValue);
- }
- if (mTargetValue != mCurrentValue) {
- postAnimationCallback();
- } else {
- mAnimating = false;
- if (mListener != null) {
- mListener.onAnimationEnd();
- }
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
}
}
- };
+ final float oldCurrentValue = mCurrentValue;
+ mCurrentValue = mAnimatedValue;
+ if (oldCurrentValue != mCurrentValue) {
+ setPropertyValue(mCurrentValue);
+ }
+ if (mTargetValue == mCurrentValue) {
+ mAnimating = false;
+ }
+ }
public interface Listener {
void onAnimationEnd();
}
static class DualRampAnimator<T> {
+ private final Choreographer mChoreographer;
private final RampAnimator<T> mFirst;
private final RampAnimator<T> mSecond;
- private final Listener mInternalListener = new Listener() {
- @Override
- public void onAnimationEnd() {
- if (mListener != null && !isAnimating()) {
- mListener.onAnimationEnd();
- }
- }
- };
private Listener mListener;
+ private boolean mAwaitingCallback;
DualRampAnimator(T object, FloatProperty<T> firstProperty,
FloatProperty<T> secondProperty) {
- mFirst = new RampAnimator(object, firstProperty);
- mFirst.setListener(mInternalListener);
- mSecond = new RampAnimator(object, secondProperty);
- mSecond.setListener(mInternalListener);
+ mChoreographer = Choreographer.getInstance();
+ mFirst = new RampAnimator<>(object, firstProperty);
+ mSecond = new RampAnimator<>(object, secondProperty);
}
/**
@@ -253,17 +211,56 @@
* @return True if either target differs from the previous target.
*/
public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
- final boolean firstRetval = mFirst.animateTo(linearFirstTarget, rate);
- final boolean secondRetval = mSecond.animateTo(linearSecondTarget, rate);
- return firstRetval && secondRetval;
+ boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
+ animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
+ boolean shouldBeAnimating = isAnimating();
+
+ if (shouldBeAnimating != mAwaitingCallback) {
+ if (shouldBeAnimating) {
+ mAwaitingCallback = true;
+ postAnimationCallback();
+ } else if (mAwaitingCallback) {
+ mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
+ mAnimationCallback, null);
+ mAwaitingCallback = false;
+ }
+ }
+ return animationTargetChanged;
}
+ /**
+ * Sets a listener to watch for animation events.
+ */
public void setListener(Listener listener) {
mListener = listener;
}
+ /**
+ * Returns true if the animation is running.
+ */
public boolean isAnimating() {
- return mFirst.isAnimating() && mSecond.isAnimating();
+ return mFirst.isAnimating() || mSecond.isAnimating();
}
+
+ private void postAnimationCallback() {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
+ }
+
+ private final Runnable mAnimationCallback = new Runnable() {
+ @Override // Choreographer callback
+ public void run() {
+ long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+ mFirst.performNextAnimationStep(frameTimeNanos);
+ mSecond.performNextAnimationStep(frameTimeNanos);
+ if (isAnimating()) {
+ postAnimationCallback();
+ } else {
+ if (mListener != null) {
+ mListener.onAnimationEnd();
+ }
+ mAwaitingCallback = false;
+ }
+ }
+ };
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 9f44765..479629e 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -35,6 +35,7 @@
import static com.android.server.display.DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.IVirtualDisplayCallback;
@@ -110,15 +111,20 @@
} else {
uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
}
+ MediaProjectionCallback mediaProjectionCallback = null;
+ if (projection != null) {
+ mediaProjectionCallback = new MediaProjectionCallback(appToken);
+ }
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ ownerUid, ownerPackageName, surface, flags,
+ new Callback(callback, mHandler), projection, mediaProjectionCallback,
uniqueId, uniqueIndex, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
try {
if (projection != null) {
- projection.registerCallback(new MediaProjectionCallback(appToken));
+ projection.registerCallback(mediaProjectionCallback);
}
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
@@ -223,6 +229,8 @@
final String mName;
private final int mFlags;
private final Callback mCallback;
+ @Nullable private final IMediaProjection mProjection;
+ @Nullable private final IMediaProjectionCallback mMediaProjectionCallback;
private int mWidth;
private int mHeight;
@@ -240,7 +248,8 @@
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
- Callback callback, String uniqueId, int uniqueIndex,
+ Callback callback, IMediaProjection projection,
+ IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
VirtualDisplayConfig virtualDisplayConfig) {
super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
mAppToken = appToken;
@@ -254,6 +263,8 @@
mSurface = surface;
mFlags = flags;
mCallback = callback;
+ mProjection = projection;
+ mMediaProjectionCallback = mediaProjectionCallback;
mDisplayState = Display.STATE_UNKNOWN;
mPendingChanges |= PENDING_SURFACE_CHANGE;
mUniqueIndex = uniqueIndex;
@@ -269,6 +280,13 @@
Slog.i(TAG, "Virtual display device released because application token died: "
+ mOwnerPackageName);
destroyLocked(false);
+ if (mProjection != null && mMediaProjectionCallback != null) {
+ try {
+ mProjection.unregisterCallback(mMediaProjectionCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unregister callback in binderDied", e);
+ }
+ }
sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
@@ -279,6 +297,13 @@
mSurface = null;
}
SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+ if (mProjection != null && mMediaProjectionCallback != null) {
+ try {
+ mProjection.unregisterCallback(mMediaProjectionCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unregister callback in destroy", e);
+ }
+ }
if (binderAlive) {
mCallback.dispatchDisplayStopped();
}
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 8180e66..0c889c2 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -32,11 +32,10 @@
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
static final boolean DEBUG = HandwritingModeController.DEBUG;
- // Place the layer below the highest layer to place it under gesture monitors. If the surface
- // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
- // is intercepting.
+ // Place the layer at the highest layer so stylus cannot trigger gesture monitors
+ // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
// TODO(b/217538817): Specify the ordering in WM by usage.
- private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
+ private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
private final InputWindowHandle mWindowHandle;
private final InputChannel mClientChannel;
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 2bdeab4..f144cf8 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -44,7 +44,6 @@
public class BiometricDeferredQueue {
private static final String TAG = "BiometricDeferredQueue";
- @NonNull private final Context mContext;
@NonNull private final SyntheticPasswordManager mSpManager;
@NonNull private final Handler mHandler;
@Nullable private FingerprintManager mFingerprintManager;
@@ -131,9 +130,7 @@
mFaceResetLockoutTask = null;
};
- BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager,
- @NonNull Handler handler) {
- mContext = context;
+ BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) {
mSpManager = spManager;
mHandler = handler;
mPendingResetLockoutsForFingerprint = new ArrayList<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b86fa7a..c5f73625 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -67,7 +67,6 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
@@ -238,7 +237,6 @@
@VisibleForTesting
protected final SyntheticPasswordManager mSpManager;
- private final KeyStore mKeyStore;
private final java.security.KeyStore mJavaKeyStore;
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private ManagedProfilePasswordCache mManagedProfilePasswordCache;
@@ -570,7 +568,6 @@
protected LockSettingsService(Injector injector) {
mInjector = injector;
mContext = injector.getContext();
- mKeyStore = injector.getKeyStore();
mJavaKeyStore = injector.getJavaKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
@@ -595,7 +592,7 @@
mSpManager = injector.getSyntheticPasswordManager(mStorage);
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
- mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
+ mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
mStorage);
@@ -637,7 +634,6 @@
}
private void showEncryptionNotificationForProfile(UserHandle user, String reason) {
- Resources r = mContext.getResources();
CharSequence title = getEncryptionNotificationTitle();
CharSequence message = getEncryptionNotificationMessage();
CharSequence detail = getEncryptionNotificationDetail();
@@ -657,7 +653,7 @@
PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
- Slog.d(TAG, String.format("showing encryption notification, user: %d; reason: %s",
+ Slog.d(TAG, TextUtils.formatSimple("showing encryption notification, user: %d; reason: %s",
user.getIdentifier(), reason));
showEncryptionNotification(user, title, message, detail, intent);
@@ -839,7 +835,7 @@
if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
}
- checkWritePermission(UserHandle.USER_SYSTEM);
+ checkWritePermission();
mHasSecureLockScreen = mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN);
@@ -979,7 +975,7 @@
}
}
- private final void checkWritePermission(int userId) {
+ private final void checkWritePermission() {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
@@ -987,7 +983,7 @@
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
}
- private final void checkPasswordHavePermission(int userId) {
+ private final void checkPasswordHavePermission() {
if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
}
@@ -1056,7 +1052,7 @@
@Override
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
LockscreenCredential profileUserPassword) {
- checkWritePermission(userId);
+ checkWritePermission();
if (!mHasSecureLockScreen
&& profileUserPassword != null
&& profileUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
@@ -1103,19 +1099,19 @@
@Override
public void setBoolean(String key, boolean value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setBoolean(key, value, userId);
}
@Override
public void setLong(String key, long value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setLong(key, value, userId);
}
@Override
public void setString(String key, String value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setString(key, value, userId);
}
@@ -1154,7 +1150,7 @@
*/
@Override
public int getCredentialType(int userId) {
- checkPasswordHavePermission(userId);
+ checkPasswordHavePermission();
return getCredentialTypeInternal(userId);
}
@@ -1627,7 +1623,7 @@
}
onSyntheticPasswordKnown(userId, sp);
- setLockCredentialWithSpLocked(credential, sp, userId, isLockTiedToParent);
+ setLockCredentialWithSpLocked(credential, sp, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
@@ -1641,15 +1637,18 @@
if (newCredential.isPattern()) {
setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
}
+ updatePasswordHistory(newCredential, userHandle);
mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
}
/**
* Store the hash of the new password in the password history list, if device policy enforces
* a password history requirement.
+ *
+ * This must not be called while the mSpManager lock is held, as this calls into
+ * DevicePolicyManagerService to get the requested password history length.
*/
- private void updatePasswordHistory(SyntheticPassword sp, LockscreenCredential password,
- int userHandle, boolean isLockTiedToParent) {
+ private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
if (password.isNone()) {
return;
}
@@ -1657,10 +1656,6 @@
// Do not keep track of historical patterns
return;
}
- if (isLockTiedToParent) {
- // Do not keep track of historical auto-generated profile passwords
- return;
- }
// Add the password to the password history.
String passwordHistory = getString(
LockPatternUtils.PASSWORD_HISTORY_KEY, /* defaultValue= */ null, userHandle);
@@ -1671,9 +1666,16 @@
if (passwordHistoryLength == 0) {
passwordHistory = "";
} else {
- final byte[] hashFactor = sp.derivePasswordHashFactor();
+ final byte[] hashFactor = getHashFactor(password, userHandle);
final byte[] salt = getSalt(userHandle).getBytes();
String hash = password.passwordToHistoryHash(salt, hashFactor);
+ if (hash == null) {
+ // This should never happen, as all information needed to compute the hash should be
+ // available. In particular, unwrapping the SP in getHashFactor() should always
+ // succeed, as we're using the LSKF that was just set.
+ Slog.e(TAG, "Failed to compute password hash; password history won't be updated");
+ return;
+ }
if (TextUtils.isEmpty(passwordHistory)) {
passwordHistory = hash;
} else {
@@ -1961,7 +1963,7 @@
@Override
public void resetKeyStore(int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
List<Integer> profileUserIds = new ArrayList<>();
List<LockscreenCredential> profileUserDecryptedPasswords = new ArrayList<>();
@@ -2269,7 +2271,7 @@
@Override
public void requireStrongAuth(int strongAuthReason, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStrongAuth.requireStrongAuth(strongAuthReason, userId);
}
@@ -2287,7 +2289,7 @@
@Override
public void userPresent(int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStrongAuth.reportUnlock(userId);
}
@@ -2315,9 +2317,9 @@
final int origPid = Binder.getCallingPid();
final int origUid = Binder.getCallingUid();
+ Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
// The original identity is an opaque integer.
final long origId = Binder.clearCallingIdentity();
- Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
try {
final LockSettingsShellCommand command =
new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid,
@@ -2644,7 +2646,7 @@
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
- SyntheticPassword sp, int userId, boolean isLockTiedToParent) {
+ SyntheticPassword sp, int userId) {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithSpLocked: user=" + userId);
final int savedCredentialType = getCredentialTypeInternal(userId);
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
@@ -2682,7 +2684,6 @@
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
- updatePasswordHistory(sp, credential, userId, isLockTiedToParent);
setUserPasswordMetrics(credential, userId);
mManagedProfilePasswordCache.removePassword(userId);
if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
@@ -2842,7 +2843,7 @@
synchronized (mSpManager) {
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
for (long handle : mSpManager.getPendingTokensForUser(userId)) {
- Slog.i(TAG, String.format("activateEscrowTokens: %x %d ", handle, userId));
+ Slog.i(TAG, TextUtils.formatSimple("activateEscrowTokens: %x %d ", handle, userId));
mSpManager.createTokenBasedProtector(handle, sp, userId);
}
}
@@ -2928,8 +2929,7 @@
return false;
}
onSyntheticPasswordKnown(userId, result.syntheticPassword);
- setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId,
- /* isLockTiedToParent= */ false);
+ setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId);
return true;
}
@@ -3004,14 +3004,14 @@
pw.println("User " + userId);
pw.increaseIndent();
synchronized (mSpManager) {
- pw.println(String.format("LSKF-based SP protector ID: %x",
+ pw.println(TextUtils.formatSimple("LSKF-based SP protector ID: %x",
getCurrentLskfBasedProtectorId(userId)));
- pw.println(String.format("LSKF last changed: %s (previous protector: %x)",
+ pw.println(TextUtils.formatSimple("LSKF last changed: %s (previous protector: %x)",
timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
}
try {
- pw.println(String.format("SID: %x",
+ pw.println(TextUtils.formatSimple("SID: %x",
getGateKeeperService().getSecureUserId(userId)));
} catch (RemoteException e) {
// ignore.
@@ -3022,7 +3022,7 @@
pw.println("CredentialType: " + credentialTypeToString(
getCredentialTypeInternal(userId)));
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
- pw.println(String.format("Metrics: %s",
+ pw.println(TextUtils.formatSimple("Metrics: %s",
getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index e5b50362..db036b0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,7 +17,6 @@
package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;
-import static android.text.TextUtils.formatSimple;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -437,7 +436,7 @@
}
private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) {
- String fileName = formatSimple("%016x.%s", protectorId, name);
+ String fileName = TextUtils.formatSimple("%016x.%s", protectorId, name);
return new File(getSyntheticPasswordDirectoryForUser(userId), fileName);
}
@@ -643,13 +642,13 @@
final UserManager um = UserManager.get(mContext);
for (UserInfo user : um.getUsers()) {
File userPath = getSyntheticPasswordDirectoryForUser(user.id);
- pw.println(String.format("User %d [%s]:", user.id, userPath));
+ pw.println(TextUtils.formatSimple("User %d [%s]:", user.id, userPath));
pw.increaseIndent();
File[] files = userPath.listFiles();
if (files != null) {
Arrays.sort(files);
for (File file : files) {
- pw.println(String.format("%6d %s %s", file.length(),
+ pw.println(TextUtils.formatSimple("%6d %s %s", file.length(),
LockSettingsService.timestampToString(file.lastModified()),
file.getName()));
}
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 17aca15..21fb403 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -72,8 +72,6 @@
/**
* Notify the manager of which slots are definitively in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void refreshActiveSlots(Set<Integer> activeSlots) throws RuntimeException {
if (mSlotMap == null) {
@@ -103,8 +101,6 @@
/**
* Mark the given slot as in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void markSlotInUse(int slot) throws RuntimeException {
ensureSlotMapLoaded();
@@ -117,8 +113,6 @@
/**
* Mark the given slot as no longer in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void markSlotDeleted(int slot) throws RuntimeException {
ensureSlotMapLoaded();
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
index da29368..41cdb42 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
@@ -64,9 +64,9 @@
private SecretKey getKeyStoreEncryptionKeyLocked() {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+ KeyStore.LoadStoreParameter loadStoreParameter =
+ new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
null);
@@ -86,9 +86,9 @@
synchronized (mKeyStoreLock) {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+ KeyStore.LoadStoreParameter loadStoreParameter =
+ new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME);
} catch (IOException | GeneralSecurityException e) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index a931844..2a6ae44 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -23,6 +23,7 @@
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
@@ -301,7 +302,7 @@
// Treat this as a success so we don't migrate again.
return true;
} else {
- Slog.e(TAG, String.format("Failed to migrate key: %d", err));
+ Slog.e(TAG, TextUtils.formatSimple("Failed to migrate key: %d", err));
return false;
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 5b75b6a..f1afb96 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -36,6 +36,7 @@
import android.security.Scrypt;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -590,7 +591,7 @@
// Remove potential persistent state (in RPMB), to prevent them from accumulating and
// causing problems.
try {
- gatekeeper.clearSecureUserId(fakeUid(userId));
+ gatekeeper.clearSecureUserId(fakeUserId(userId));
} catch (RemoteException ignore) {
Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
@@ -800,13 +801,13 @@
// In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
// to prevent them from accumulating and causing problems.
try {
- gatekeeper.clearSecureUserId(fakeUid(userId));
+ gatekeeper.clearSecureUserId(fakeUserId(userId));
} catch (RemoteException ignore) {
Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
GateKeeperResponse response;
try {
- response = gatekeeper.enroll(fakeUid(userId), null, null,
+ response = gatekeeper.enroll(fakeUserId(userId), null, null,
stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
@@ -840,7 +841,7 @@
GateKeeperResponse response;
try {
- response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
+ response = gatekeeper.verifyChallenge(fakeUserId(persistentData.userId),
0 /* challenge */, pwd.passwordHandle,
stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
@@ -1029,7 +1030,7 @@
userId));
if (!credential.checkAgainstStoredType(pwd.credentialType)) {
- Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
+ Slog.e(TAG, TextUtils.formatSimple("Credential type mismatch: expected %d actual %d",
pwd.credentialType, credential.getType()));
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
@@ -1059,7 +1060,7 @@
byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
GateKeeperResponse response;
try {
- response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
+ response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
pwd.passwordHandle, gkPassword);
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
@@ -1072,7 +1073,7 @@
if (response.getShouldReEnroll()) {
GateKeeperResponse reenrollResponse;
try {
- reenrollResponse = gatekeeper.enroll(fakeUid(userId),
+ reenrollResponse = gatekeeper.enroll(fakeUserId(userId),
pwd.passwordHandle, gkPassword, gkPassword);
} catch (RemoteException e) {
Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
@@ -1452,8 +1453,8 @@
return result;
}
- private int fakeUid(int uid) {
- return 100000 + uid;
+ private int fakeUserId(int userId) {
+ return 100000 + userId;
}
protected static byte[] secureRandom(int length) {
@@ -1466,7 +1467,7 @@
}
private String getProtectorKeyAlias(long protectorId) {
- return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
+ return TextUtils.formatSimple("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
}
private byte[] stretchLskf(LockscreenCredential credential, PasswordData data) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index f32af54..7009a41 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -75,7 +75,6 @@
"com.android.server.locksettings.recoverablekeystore/platform/";
private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
- private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH_BITS = 128;
// Only used for checking if a key is usable
@@ -184,7 +183,6 @@
invalidatePlatformKey(userId, generationId);
nextId = generationId + 1;
}
- generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED);
generateAndLoadKey(userId, nextId);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
index be35b50..7d5fd65 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
@@ -35,7 +35,6 @@
public class CleanupManager {
private static final String TAG = "CleanupManager";
- private final Context mContext;
private final UserManager mUserManager;
private final RecoverableKeyStoreDb mDatabase;
private final RecoverySnapshotStorage mSnapshotStorage;
@@ -54,7 +53,6 @@
RecoverableKeyStoreDb recoverableKeyStoreDb,
ApplicationKeyStorage applicationKeyStorage) {
return new CleanupManager(
- context,
snapshotStorage,
recoverableKeyStoreDb,
UserManager.get(context),
@@ -63,12 +61,10 @@
@VisibleForTesting
CleanupManager(
- Context context,
RecoverySnapshotStorage snapshotStorage,
RecoverableKeyStoreDb recoverableKeyStoreDb,
UserManager userManager,
ApplicationKeyStorage applicationKeyStorage) {
- mContext = context;
mSnapshotStorage = snapshotStorage;
mDatabase = recoverableKeyStoreDb;
mUserManager = userManager;
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index f5910fa..718756f 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.IntDef;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
@@ -30,6 +31,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
@@ -41,13 +44,52 @@
private static final String ATTR_FLAGS = "flags";
private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
private static final String ATTR_FILTER = "filter";
+ private static final String ATTR_ACCESS_CONTROL = "accessControl";
private static final String TAG = "CrossProfileIntentFilter";
+ /**
+ * AccessControlLevel provides level of access for user to create/modify
+ * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}.
+ * Each AccessControlLevel have value assigned, the higher the value
+ * implies higher restriction for creation/modification.
+ * AccessControlLevel allows us to protect against malicious changes in user's
+ * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter}
+ * leading to unprecedented results.
+ */
+ @IntDef(prefix = {"ACCESS_LEVEL_"}, value = {
+ ACCESS_LEVEL_ALL,
+ ACCESS_LEVEL_SYSTEM,
+ ACCESS_LEVEL_SYSTEM_ADD_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AccessControlLevel {
+ }
+
+ /**
+ * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow
+ * access(addition/modification/removal) for CrossProfileIntentFilter.
+ * This is the default access control level.
+ */
+ public static final int ACCESS_LEVEL_ALL = 0;
+
+ /**
+ * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to
+ * access(addition/modification/removal) CrossProfileIntentFilter.
+ */
+ public static final int ACCESS_LEVEL_SYSTEM = 10;
+
+ /**
+ * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add
+ * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed.
+ */
+ public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20;
+
// If the intent matches the IntentFilter, then it can be forwarded to this userId.
final int mTargetUserId;
final String mOwnerPackage; // packageName of the app.
final int mFlags;
+ final int mAccessControlLevel;
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
@@ -65,10 +107,16 @@
CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
int flags) {
+ this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL);
+ }
+
+ CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
+ int flags, @AccessControlLevel int accessControlLevel) {
super(filter);
mTargetUserId = targetUserId;
mOwnerPackage = ownerPackage;
mFlags = flags;
+ mAccessControlLevel = accessControlLevel;
mSnapshot = makeCache();
}
@@ -77,12 +125,18 @@
this(filter.mFilter, ownerPackage, targetUserId, flags);
}
+ CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId,
+ int flags, @AccessControlLevel int accessControlLevel) {
+ this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel);
+ }
+
// Copy constructor used only to create a snapshot.
private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
super(f);
mTargetUserId = f.mTargetUserId;
mOwnerPackage = f.mOwnerPackage;
mFlags = f.mFlags;
+ mAccessControlLevel = f.mAccessControlLevel;
mSnapshot = new SnapshotCache.Sealed();
}
@@ -98,9 +152,14 @@
return mOwnerPackage;
}
+ public @AccessControlLevel int getAccessControlLevel() {
+ return mAccessControlLevel;
+ }
+
CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
+ mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL);
mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
mSnapshot = makeCache();
@@ -151,6 +210,7 @@
serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId);
serializer.attributeInt(null, ATTR_FLAGS, mFlags);
serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
+ serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel);
serializer.startTag(null, ATTR_FILTER);
mFilter.writeToXml(serializer);
serializer.endTag(null, ATTR_FILTER);
@@ -165,7 +225,8 @@
boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
return mTargetUserId == other.mTargetUserId
&& mOwnerPackage.equals(other.mOwnerPackage)
- && mFlags == other.mFlags;
+ && mFlags == other.mFlags
+ && mAccessControlLevel == other.mAccessControlLevel;
}
public CrossProfileIntentFilter snapshot() {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 48ec436..bda7b82 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -816,9 +816,10 @@
}
// Allow package verifier to silently uninstall.
- if (mPm.mRequiredVerifierPackage != null && callingUid == snapshot
- .getPackageUid(mPm.mRequiredVerifierPackage, 0, callingUserId)) {
- return true;
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (callingUid == snapshot.getPackageUid(verifierPackageName, 0, callingUserId)) {
+ return true;
+ }
}
// Allow package uninstaller to silently uninstall.
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 7352018..d2a5e32 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -54,7 +54,7 @@
private final StorageEventHelper mStorageEventHelper;
private final DomainVerificationManagerInternal mDomainVerificationManager;
private final PackageInstallerService mInstallerService;
- private final String mRequiredVerifierPackage;
+ private final String[] mRequiredVerifierPackages;
private final KnownPackages mKnownPackages;
private final ChangedPackagesTracker mChangedPackagesTracker;
private final ArrayMap<String, FeatureInfo> mAvailableFeatures;
@@ -65,7 +65,7 @@
PermissionManagerServiceInternal permissionManager,
StorageEventHelper storageEventHelper,
DomainVerificationManagerInternal domainVerificationManager,
- PackageInstallerService installerService, String requiredVerifierPackage,
+ PackageInstallerService installerService, String[] requiredVerifierPackages,
KnownPackages knownPackages,
ChangedPackagesTracker changedPackagesTracker,
ArrayMap<String, FeatureInfo> availableFeatures,
@@ -75,7 +75,7 @@
mStorageEventHelper = storageEventHelper;
mDomainVerificationManager = domainVerificationManager;
mInstallerService = installerService;
- mRequiredVerifierPackage = requiredVerifierPackage;
+ mRequiredVerifierPackages = requiredVerifierPackages;
mKnownPackages = knownPackages;
mChangedPackagesTracker = changedPackagesTracker;
mAvailableFeatures = availableFeatures;
@@ -313,26 +313,28 @@
ipw.decreaseIndent();
}
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
- && packageName == null) {
- final String requiredVerifierPackage = mRequiredVerifierPackage;
- if (!checkin) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (!checkin && mRequiredVerifierPackages.length > 0) {
if (dumpState.onTitlePrinted()) {
pw.println();
}
pw.println("Verifiers:");
- pw.print(" Required: ");
- pw.print(requiredVerifierPackage);
- pw.print(" (uid=");
- pw.print(snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
- pw.println(")");
- } else if (requiredVerifierPackage != null) {
- pw.print("vrfy,");
- pw.print(requiredVerifierPackage);
- pw.print(",");
- pw.println(snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ }
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ if (!checkin) {
+ pw.print(" Required: ");
+ pw.print(requiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ pw.println(")");
+ } else {
+ pw.print("vrfy,");
+ pw.print(requiredVerifierPackage);
+ pw.print(",");
+ pw.println(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ }
}
}
@@ -642,17 +644,19 @@
private void dumpProto(Computer snapshot, FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- final long requiredVerifierPackageToken =
- proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
- proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
- mRequiredVerifierPackage);
- proto.write(
- PackageServiceDumpProto.PackageShortProto.UID,
- snapshot.getPackageUid(
- mRequiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- proto.end(requiredVerifierPackageToken);
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ final long requiredVerifierPackageToken =
+ proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
+ proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
+ requiredVerifierPackage);
+ proto.write(
+ PackageServiceDumpProto.PackageShortProto.UID,
+ snapshot.getPackageUid(
+ requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ proto.end(requiredVerifierPackageToken);
+ }
DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
ComponentName verifierComponent = proxy.getComponentName();
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 122ab10..52e3fd9 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -46,6 +46,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
@@ -401,4 +402,9 @@
public List<ScanPartition> getDirsToScanAsSystem() {
return mDirsToScanAsSystem;
}
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public int getSystemScanFlags() {
+ return mSystemScanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8bb9b84..728d2d1 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2719,15 +2719,16 @@
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
}
- // if the required verifier is defined, but, is not the installer of record
- // for the package, it gets notified
- final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
- && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ // Notify required verifier(s) that are not the installer of record for the package.
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ verifierPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */,
+ null);
+ }
}
// If package installer is defined, notify package installer about new
// app installed
@@ -2752,12 +2753,14 @@
updateUserIds, instantUserIds, null /*broadcastAllowList*/,
null);
}
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/, verifierPackageName,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/, null);
+ }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java
index 04376b4..dcf7152 100644
--- a/services/core/java/com/android/server/pm/KnownPackages.java
+++ b/services/core/java/com/android/server/pm/KnownPackages.java
@@ -79,7 +79,7 @@
private final String mRequiredInstallerPackage;
private final String mRequiredUninstallerPackage;
private final String mSetupWizardPackage;
- private final String mRequiredVerifierPackage;
+ private final String[] mRequiredVerifierPackages;
private final String mDefaultTextClassifierPackage;
private final String mSystemTextClassifierPackageName;
private final String mRequiredPermissionControllerPackage;
@@ -94,7 +94,7 @@
KnownPackages(DefaultAppProvider defaultAppProvider, String requiredInstallerPackage,
String requiredUninstallerPackage, String setupWizardPackage,
- String requiredVerifierPackage, String defaultTextClassifierPackage,
+ String[] requiredVerifierPackages, String defaultTextClassifierPackage,
String systemTextClassifierPackageName, String requiredPermissionControllerPackage,
String configuratorPackage, String incidentReportApproverPackage,
String ambientContextDetectionPackage, String appPredictionServicePackage,
@@ -104,7 +104,7 @@
mRequiredInstallerPackage = requiredInstallerPackage;
mRequiredUninstallerPackage = requiredUninstallerPackage;
mSetupWizardPackage = setupWizardPackage;
- mRequiredVerifierPackage = requiredVerifierPackage;
+ mRequiredVerifierPackages = requiredVerifierPackages;
mDefaultTextClassifierPackage = defaultTextClassifierPackage;
mSystemTextClassifierPackageName = systemTextClassifierPackageName;
mRequiredPermissionControllerPackage = requiredPermissionControllerPackage;
@@ -182,7 +182,7 @@
case PACKAGE_SYSTEM:
return new String[]{"android"};
case PACKAGE_VERIFIER:
- return snapshot.filterOnlySystemPackages(mRequiredVerifierPackage);
+ return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages);
case PACKAGE_SYSTEM_TEXT_CLASSIFIER:
return snapshot.filterOnlySystemPackages(
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 32b3e6a..e798adf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,6 +238,7 @@
import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
import java.io.ByteArrayInputStream;
@@ -521,6 +522,9 @@
private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";
+ // How many required verifiers can be on the system.
+ private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
+
// Compilation reasons.
public static final int REASON_FIRST_BOOT = 0;
public static final int REASON_BOOT_AFTER_OTA = 1;
@@ -906,7 +910,7 @@
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
- final @Nullable String mRequiredVerifierPackage;
+ final @NonNull String[] mRequiredVerifierPackages;
final @NonNull String mRequiredInstallerPackage;
final @NonNull String mRequiredUninstallerPackage;
final @NonNull String mRequiredPermissionControllerPackage;
@@ -1642,7 +1646,7 @@
mProtectedPackages = testParams.protectedPackages;
mSeparateProcesses = testParams.separateProcesses;
mViewCompiler = testParams.viewCompiler;
- mRequiredVerifierPackage = testParams.requiredVerifierPackage;
+ mRequiredVerifierPackages = testParams.requiredVerifierPackages;
mRequiredInstallerPackage = testParams.requiredInstallerPackage;
mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
@@ -2126,7 +2130,7 @@
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
- mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+ mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer);
mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
ComponentName intentFilterVerifierComponent =
@@ -2265,8 +2269,8 @@
"persist.pm.mock-upgrade", false /* default */);
}
- @Nullable
- private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
+ @NonNull
+ private String[] getRequiredButNotReallyRequiredVerifiersLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List<ResolveInfo> matches =
@@ -2274,13 +2278,23 @@
PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, Binder.getCallingUid());
- if (matches.size() == 1) {
- return matches.get(0).getComponentInfo().packageName;
- } else if (matches.size() == 0) {
+ final int size = matches.size();
+ if (size == 0) {
Log.w(TAG, "There should probably be a verifier, but, none were found");
- return null;
+ return EmptyArray.STRING;
+ } else if (size <= REQUIRED_VERIFIERS_MAX_COUNT) {
+ String[] verifiers = new String[size];
+ for (int i = 0; i < size; ++i) {
+ verifiers[i] = matches.get(i).getComponentInfo().packageName;
+ if (TextUtils.isEmpty(verifiers[i])) {
+ throw new RuntimeException("Invalid verifier: " + matches);
+ }
+ }
+ return verifiers;
}
- throw new RuntimeException("There must be exactly one verifier; found " + matches);
+ throw new RuntimeException(
+ "There must be no more than " + REQUIRED_VERIFIERS_MAX_COUNT + " verifiers; found "
+ + matches);
}
@NonNull
@@ -3153,8 +3167,12 @@
boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
- return mRequiredVerifierPackage != null && callingUid == snapshot.getPackageUid(
- mRequiredVerifierPackage, 0, callingUserId);
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ if (callingUid == snapshot.getPackageUid(requiredVerifierPackage, 0, callingUserId)) {
+ return true;
+ }
+ }
+ return false;
}
public boolean isPackageDeviceAdminOnAnyUser(@NonNull Computer snapshot, String packageName) {
@@ -3332,6 +3350,12 @@
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
enforceOwnerRights(snapshot, ownerPackage, callingUid);
+
+ // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter}
+ // for source and target user
+ mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid,
+ /* addCrossProfileIntentFilter */ true);
+
PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
if (intentFilter.countActions() == 0) {
@@ -3340,7 +3364,8 @@
}
synchronized (mLock) {
CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
- ownerPackage, targetUserId, flags);
+ ownerPackage, targetUserId, flags, mUserManager
+ .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId));
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
@@ -4554,7 +4579,11 @@
ArraySet<CrossProfileIntentFilter> set =
new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
- if (filter.getOwnerPackage().equals(ownerPackage)) {
+ //Only remove if calling user is allowed based on access control of
+ // {@link CrossProfileIntentFilter}
+ if (filter.getOwnerPackage().equals(ownerPackage)
+ && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId,
+ filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) {
resolver.removeFilter(filter);
}
}
@@ -4665,15 +4694,28 @@
}
@Override
- public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ public void extendVerificationTimeout(int verificationId, int verificationCodeAtTimeout,
long millisecondsToDelay) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can extend verification timeouts");
+ // Negative ids correspond to testing verifiers and will be silently enforced in
+ // the handler thread.
+ if (verificationId >= 0) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can extend verification timeouts");
+ }
final int callingUid = Binder.getCallingUid();
mHandler.post(() -> {
+ final int id = verificationId >= 0 ? verificationId : -verificationId;
final PackageVerificationState state = mPendingVerification.get(id);
+ if (state == null || state.timeoutExtended() || !state.checkRequiredVerifierUid(
+ callingUid)) {
+ // Only allow calls from required verifiers.
+ return;
+ }
+
+ state.extendTimeout();
+
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCodeAtTimeout, callingUid);
@@ -4685,14 +4727,10 @@
delay = 0;
}
- if ((state != null) && !state.timeoutExtended()) {
- state.extendTimeout();
-
- final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessageDelayed(msg, delay);
- }
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, delay);
});
}
@@ -5434,13 +5472,19 @@
final long callingId = Binder.clearCallingIdentity();
try {
- final PackageStateInternal packageState =
- snapshot.getPackageStateForInstalledAndFiltered(
- packageName, callingUid, userId);
+ final PackageStateInternal packageState = snapshot.getPackageStateInternal(
+ packageName);
if (packageState == null) {
return false;
}
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(
+ userId);
+ if (userState.isHidden() == hidden || !userState.isInstalled()
+ || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
+ return false;
+ }
+
// Cannot hide static shared libs as they are considered
// a part of the using app (emulating static linking). Also
// static libs are installed always on internal storage.
@@ -5470,10 +5514,6 @@
return false;
}
- if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
- return false;
- }
-
commitPackageStateMutation(null, packageName, packageState1 ->
packageState1.userState(userId).setHidden(hidden));
@@ -5862,18 +5902,32 @@
}
@Override
- public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can verify applications");
+ public void verifyPendingInstall(int verificationId, int verificationCode)
+ throws RemoteException {
+ // Negative ids correspond to testing verifiers and will be silently enforced in
+ // the handler thread.
+ if (verificationId >= 0) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can verify applications");
+ }
final int callingUid = Binder.getCallingUid();
- final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCode, callingUid);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessage(msg);
+ mHandler.post(() -> {
+ final int id = verificationId >= 0 ? verificationId : -verificationId;
+ final PackageVerificationState state = mPendingVerification.get(id);
+ if (state == null || !state.checkRequiredVerifierUid(callingUid)) {
+ // Only allow calls from required verifiers.
+ return;
+ }
+
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCode, callingUid);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessage(msg);
+ });
}
@Override
@@ -5928,7 +5982,7 @@
mRequiredInstallerPackage,
mRequiredUninstallerPackage,
mSetupWizardPackage,
- mRequiredVerifierPackage,
+ mRequiredVerifierPackages,
mDefaultTextClassifierPackage,
mSystemTextClassifierPackageName,
mRequiredPermissionControllerPackage,
@@ -5949,7 +6003,7 @@
protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts);
}
new DumpHelper(mPermissionManager, mStorageEventHelper,
- mDomainVerificationManager, mInstallerService, mRequiredVerifierPackage,
+ mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages,
knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts,
getPerUidReadTimeouts(snapshot)
).doDump(snapshot, fd, pw, args);
@@ -6956,7 +7010,7 @@
mRequiredInstallerPackage,
mRequiredUninstallerPackage,
mSetupWizardPackage,
- mRequiredVerifierPackage,
+ mRequiredVerifierPackages,
mDefaultTextClassifierPackage,
mSystemTextClassifierPackageName,
mRequiredPermissionControllerPackage,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index f44d922..ec1b29e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -80,7 +80,7 @@
public @NonNull String requiredInstallerPackage;
public @NonNull String requiredPermissionControllerPackage;
public @NonNull String requiredUninstallerPackage;
- public @Nullable String requiredVerifierPackage;
+ public @NonNull String[] requiredVerifierPackages;
public String[] separateProcesses;
public @NonNull String servicesExtensionPackageName;
public @Nullable String setupWizardPackage;
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index d105fc4..a574fc9 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -30,7 +30,7 @@
private final SparseBooleanArray mSufficientVerifierUids;
- private int mRequiredVerifierUid;
+ private final SparseBooleanArray mRequiredVerifierUids;
private boolean mSufficientVerificationComplete;
@@ -51,6 +51,9 @@
PackageVerificationState(VerifyingSession verifyingSession) {
mVerifyingSession = verifyingSession;
mSufficientVerifierUids = new SparseBooleanArray();
+ mRequiredVerifierUids = new SparseBooleanArray();
+ mRequiredVerificationComplete = false;
+ mRequiredVerificationPassed = true;
mExtendedTimeout = false;
}
@@ -58,9 +61,14 @@
return mVerifyingSession;
}
- /** Sets the user ID of the required package verifier. */
- void setRequiredVerifierUid(int uid) {
- mRequiredVerifierUid = uid;
+ /** Add the user ID of the required package verifier. */
+ void addRequiredVerifierUid(int uid) {
+ mRequiredVerifierUids.put(uid, true);
+ }
+
+ /** Returns true if the uid a required verifier. */
+ boolean checkRequiredVerifierUid(int uid) {
+ return mRequiredVerifierUids.get(uid, false);
}
/**
@@ -80,39 +88,55 @@
* @return {@code true} if the verifying agent actually exists in our list
*/
boolean setVerifierResponse(int uid, int code) {
- if (uid == mRequiredVerifierUid) {
- mRequiredVerificationComplete = true;
+ if (mRequiredVerifierUids.get(uid)) {
switch (code) {
case PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT:
mSufficientVerifierUids.clear();
// fall through
case PackageManager.VERIFICATION_ALLOW:
- mRequiredVerificationPassed = true;
+ // Two possible options:
+ // - verification result is true,
+ // - another verifier set it to false.
+ // In both cases we don't need to assign anything, just exit.
break;
default:
mRequiredVerificationPassed = false;
}
- return true;
- } else {
- if (mSufficientVerifierUids.get(uid)) {
- if (code == PackageManager.VERIFICATION_ALLOW) {
- mSufficientVerificationComplete = true;
- mSufficientVerificationPassed = true;
- }
- mSufficientVerifierUids.delete(uid);
- if (mSufficientVerifierUids.size() == 0) {
- mSufficientVerificationComplete = true;
- }
-
- return true;
+ mRequiredVerifierUids.delete(uid);
+ if (mRequiredVerifierUids.size() == 0) {
+ mRequiredVerificationComplete = true;
}
+ return true;
+ } else if (mSufficientVerifierUids.get(uid)) {
+ if (code == PackageManager.VERIFICATION_ALLOW) {
+ mSufficientVerificationPassed = true;
+ mSufficientVerificationComplete = true;
+ }
+
+ mSufficientVerifierUids.delete(uid);
+ if (mSufficientVerifierUids.size() == 0) {
+ mSufficientVerificationComplete = true;
+ }
+
+ return true;
}
return false;
}
/**
+ * Mark the session as passed required verification.
+ */
+ void passRequiredVerification() {
+ if (mRequiredVerifierUids.size() > 0) {
+ throw new RuntimeException("Required verifiers still present.");
+ }
+ mRequiredVerificationPassed = true;
+ mRequiredVerificationComplete = true;
+ }
+
+ /**
* Returns whether verification is considered complete. This means that the required verifier
* and at least one of the sufficient verifiers has returned a positive verification.
*
@@ -137,15 +161,15 @@
* @return {@code true} if installation should be allowed
*/
boolean isInstallAllowed() {
- if (!mRequiredVerificationComplete) {
+ if (!mRequiredVerificationComplete || !mRequiredVerificationPassed) {
return false;
}
if (mSufficientVerificationComplete) {
- return mRequiredVerificationPassed && mSufficientVerificationPassed;
+ return mSufficientVerificationPassed;
}
- return mRequiredVerificationPassed;
+ return true;
}
/** Extend the timeout for this Package to be verified. */
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index f931ba8..01aecd9 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -126,10 +126,9 @@
unmodifiablePackages.add(packageName);
continue;
}
- final PackageStateInternal packageState =
- snapshot.getPackageStateForInstalledAndFiltered(
- packageName, callingUid, userId);
- if (packageState == null) {
+ final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
+ if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
+ || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping suspending/un-suspending.");
unmodifiablePackages.add(packageName);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e022f15..a0335e8 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1650,11 +1650,40 @@
}
int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
- // TODO(b/179163496): should return true for profile users of the current user as well
return currentUser == userId;
}
@Override
+ public boolean isUserVisible(@UserIdInt int userId) {
+ int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId
+ && !hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException("Caller from user " + callingUserId + " needs MANAGE_USERS "
+ + "or INTERACT_ACROSS_USERS permission to check if another user (" + userId
+ + ") is visible");
+ }
+
+ // First check current foreground user (on main display)
+ int currentUserId = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
+
+ if (currentUserId == userId) {
+ return true;
+ }
+
+ // Then profiles of current user
+ // TODO(b/239824814): consider using AMInternal.isCurrentProfile() instead
+ if (isProfile(userId)) {
+ int parentId = Binder.withCleanCallingIdentity(() -> getProfileParentId(userId));
+ if (parentId == currentUserId) {
+ return isUserRunning(userId);
+ }
+ }
+
+ // TODO(b/239824814): support background users on secondary display (and their profiles)
+ return false;
+ }
+
+ @Override
public @NonNull String getUserName() {
final int callingUid = Binder.getCallingUid();
if (!hasQueryOrCreateUsersPermission()
@@ -1866,6 +1895,103 @@
return mLocalService.exists(userId);
}
+ /**
+ * Returns user's {@link CrossProfileIntentFilter.AccessControlLevel}, which is derived from
+ * {@link UserTypeDetails}. If user does not have defined their access control level,
+ * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ */
+ private @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl()
+ : CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
+ }
+
+ /**
+ * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given
+ * source and target user.
+ * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+ * @param targetUserId target user where we can resolve given intent filter
+ * @param callingUid user accessing api
+ * @param addCrossProfileIntentFilter if the operation is addition or not.
+ * @throws SecurityException is calling user is not allowed to access.
+ */
+ public void enforceCrossProfileIntentFilterAccess(
+ int sourceUserId, int targetUserId,
+ int callingUid, boolean addCrossProfileIntentFilter) {
+ if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId,
+ addCrossProfileIntentFilter)) {
+ throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user "
+ + callingUid);
+ }
+ }
+
+ /**
+ * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source
+ * and target user. There are following rules of access
+ * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL},
+ * irrespective of user we would allow access(addition/modification/removal)
+ * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM},
+ * only system/root user would be able to access(addition/modification/removal)
+ * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY},
+ * only system/root user would be able to add but not modify/remove. Once added, it cannot be
+ * modified or removed
+ * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+ * @param targetUserId target user where we can resolve given intent filter
+ * @param addCrossProfileIntentFilter if the operation is addition or not.
+ * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user
+ */
+ public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId,
+ boolean addCrossProfileIntentFilter) {
+ int effectiveAccessControl =
+ getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId);
+
+ /*
+ For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not
+ system or root disallowing access to {@link CrossProfileIntentFilter}
+ */
+ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl
+ && !PackageManagerServiceUtils.isSystemOrRoot()) {
+ return false;
+ }
+
+ /*
+ For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only
+ system user to add {@link CrossProfileIntentFilter}. All users(including system) are
+ disallowed to modify/remove.
+ */
+ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl
+ && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@link CrossProfileIntentFilter.AccessControlLevel}
+ * that should be assigned to {@link CrossProfileIntentFilter}
+ * computed from source user's and target user's
+ * {@link CrossProfileIntentFilter.AccessControlLevel}.
+ * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge
+ * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured
+ * between Primary user and Clone profile should have access level of
+ * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest
+ * access value from source or target. The higher value means higher restrictions.
+ * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored
+ * @param targetUserId userId of target user for whom Cross Profile access would be allowed
+ * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or
+ * target user.
+ */
+ public @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) {
+ int sourceAccessControlLevel,
+ targetAccessControlLevel, effectiveAccessControl;
+ sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId);
+ targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId);
+ effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel);
+ return effectiveAccessControl;
+ }
+
@Override
public void setUserName(@UserIdInt int userId, String name) {
checkManageUsersPermission("rename users");
@@ -4556,34 +4682,31 @@
}
final List<UserInfo> users = getUsersInternal(true, true, true);
final int size = users.size();
- for (int idx = 0; idx < size; idx++) {
- final UserInfo user = users.get(idx);
- if (user.id == UserHandle.USER_SYSTEM) {
- // Skip user 0. It's not interesting. We already know it exists, is running, and (if
- // we know the device configuration) its userType.
- continue;
+ if (size > 1) {
+ for (int idx = 0; idx < size; idx++) {
+ final UserInfo user = users.get(idx);
+ final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+ final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
+ ?
+ user.userType : null;
+
+ boolean isUserRunningUnlocked;
+ synchronized (mUserStates) {
+ isUserRunningUnlocked =
+ mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ }
+
+ data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+ user.id,
+ userTypeStandard,
+ userTypeCustom,
+ user.flags,
+ user.creationTime,
+ user.lastLoggedInTime,
+ isUserRunningUnlocked
+ ));
}
-
- final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
- final String userTypeCustom = (userTypeStandard ==
- FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
- user.userType : null;
-
- boolean isUserRunningUnlocked;
- synchronized (mUserStates) {
- isUserRunningUnlocked =
- mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
- }
-
- data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
- user.id,
- userTypeStandard,
- userTypeCustom,
- user.flags,
- user.creationTime,
- user.lastLoggedInTime,
- isUserRunningUnlocked
- ));
}
return android.app.StatsManager.PULL_SUCCESS;
}
@@ -6010,6 +6133,10 @@
} // synchronized (mPackagesLock)
+ // Multiple Users on Multiple Display info
+ pw.println(" Supports users on secondary displays: "
+ + UserManager.isUsersOnSecondaryDisplaysEnabled());
+
// Dump some capabilities
pw.println();
pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 4aad1a7..2f3ca66 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -163,6 +163,14 @@
*/
private final boolean mIsCredentialSharableWithParent;
+ /**
+ * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile.
+ *
+ * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ */
+ private final @CrossProfileIntentFilter.AccessControlLevel int
+ mCrossProfileIntentFilterAccessControl;
+
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
int maxAllowedPerParent,
@@ -174,7 +182,8 @@
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
boolean isMediaSharedWithParent,
- boolean isCredentialSharableWithParent) {
+ boolean isCredentialSharableWithParent,
+ @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -195,6 +204,7 @@
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
this.mIsMediaSharedWithParent = isMediaSharedWithParent;
this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
+ this.mCrossProfileIntentFilterAccessControl = accessControlLevel;
}
/**
@@ -327,6 +337,16 @@
return mIsCredentialSharableWithParent;
}
+ /**
+ * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly
+ * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ * @return user's {@link CrossProfileIntentFilter.AccessControlLevel}
+ */
+ public @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl() {
+ return mCrossProfileIntentFilterAccessControl;
+ }
+
/** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
return BundleUtils.clone(mDefaultRestrictions);
@@ -420,6 +440,8 @@
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
private boolean mIsMediaSharedWithParent = false;
private boolean mIsCredentialSharableWithParent = false;
+ private @CrossProfileIntentFilter.AccessControlLevel int
+ mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
public Builder setName(String name) {
mName = name;
@@ -520,6 +542,16 @@
}
/**
+ * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user.
+ * @param accessControlLevel default access control for user
+ */
+ public Builder setCrossProfileIntentFilterAccessControl(
+ @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
+ mCrossProfileIntentFilterAccessControl = accessControlLevel;
+ return this;
+ }
+
+ /**
* Sets shared media property for the user.
* @param isCredentialSharableWithParent the value to be set, true or false
*/
@@ -571,7 +603,8 @@
mDefaultSecureSettings,
mDefaultCrossProfileIntentFilters,
mIsMediaSharedWithParent,
- mIsCredentialSharableWithParent);
+ mIsCredentialSharableWithParent,
+ mCrossProfileIntentFilterAccessControl);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 0878da4..857a975 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -122,6 +122,8 @@
.setLabel(0)
.setDefaultRestrictions(null)
.setIsMediaSharedWithParent(true)
+ .setCrossProfileIntentFilterAccessControl(
+ CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM)
.setIsCredentialSharableWithParent(true);
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 9bad4a8..0a39e64 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -353,45 +353,54 @@
}
final int verifierUserId = verifierUser.getIdentifier();
- String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
- boolean requiredVerifierPackageOverridden = false;
+ String[] requiredVerifierPackages = mPm.mRequiredVerifierPackages;
+ boolean requiredVerifierPackagesOverridden = false;
// Allow verifier override for ADB installations which could already be unverified using
// PackageManager.INSTALL_DISABLE_VERIFICATION flag.
if ((mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0
&& (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) == 0) {
- final String adbVerifierOverridePackage = SystemProperties.get(
- "debug.pm.adb_verifier_override_package", "");
- // Check if the package installed.
- if (!TextUtils.isEmpty(adbVerifierOverridePackage)
- && packageExists(adbVerifierOverridePackage)) {
- // Pretend we requested to disable verification from command line.
- boolean requestedDisableVerification = true;
- // If this returns false then the caller can already skip verification, so we are
- // not adding a new way to disable verifications.
- if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
- requestedDisableVerification)) {
- requiredVerifierPackage = adbVerifierOverridePackage;
- requiredVerifierPackageOverridden = true;
+ String property = SystemProperties.get("debug.pm.adb_verifier_override_packages", "");
+ if (!TextUtils.isEmpty(property)) {
+ String[] verifierPackages = property.split(";");
+ List<String> adbVerifierOverridePackages = new ArrayList<>();
+ for (String verifierPackage : verifierPackages) {
+ if (!TextUtils.isEmpty(verifierPackage) && packageExists(verifierPackage)) {
+ adbVerifierOverridePackages.add(verifierPackage);
+ }
+ }
+ // Check if the package installed.
+ if (adbVerifierOverridePackages.size() > 0) {
+ // Pretend we requested to disable verification from command line.
+ boolean requestedDisableVerification = true;
+ // If this returns false then the caller can already skip verification, so we
+ // are not adding a new way to disable verifications.
+ if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
+ requestedDisableVerification)) {
+ requiredVerifierPackages = adbVerifierOverridePackages.toArray(
+ new String[adbVerifierOverridePackages.size()]);
+ requiredVerifierPackagesOverridden = true;
+ }
}
}
}
+ if (mOriginInfo.mExisting || !isVerificationEnabled(pkgLite, verifierUserId,
+ requiredVerifierPackages)) {
+ verificationState.passRequiredVerification();
+ return;
+ }
+
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final Computer snapshot = mPm.snapshotComputer();
- final int requiredUid = requiredVerifierPackage == null ? -1
- : snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
- verificationState.setRequiredVerifierUid(requiredUid);
- final boolean isVerificationEnabled = isVerificationEnabled(pkgLite,
- verifierUserId);
- if (mOriginInfo.mExisting || !isVerificationEnabled) {
- verificationState.setVerifierResponse(requiredUid, PackageManager.VERIFICATION_ALLOW);
- return;
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
+ verificationState.addRequiredVerifierUid(requiredUid);
}
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
@@ -428,11 +437,13 @@
final String baseCodePath = mPackageLite.getBaseApkPath();
final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
+ final String rootHashString;
if (IncrementalManager.isIncrementalPath(baseCodePath)) {
- final String rootHashString =
- PackageManagerServiceUtils.buildVerificationRootHashString(
- baseCodePath, splitCodePaths);
+ rootHashString = PackageManagerServiceUtils.buildVerificationRootHashString(
+ baseCodePath, splitCodePaths);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ } else {
+ rootHashString = null;
}
verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
@@ -503,8 +514,8 @@
}
}
- if (requiredVerifierPackage == null) {
- Slog.e(TAG, "Required verifier is null");
+ if (requiredVerifierPackages.length == 0) {
+ Slog.e(TAG, "No required verifiers");
return;
}
@@ -514,47 +525,75 @@
} else {
verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
}
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, requiredUid);
- /*
- * Send the intent to the required verification agent,
- * but only start the verification timeout after the
- * target BroadcastReceivers have run.
- */
- if (!requiredVerifierPackageOverridden) {
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- requiredVerifierPackage, receivers.getList());
- verification.setComponent(requiredVerifierComponent);
- } else {
- verification.setPackage(requiredVerifierPackage);
- }
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- requiredVerifierPackage, verificationTimeout,
- verifierUserId, false,
- REASON_PACKAGE_VERIFIER, "package verifier");
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
- if (streaming) {
- // For streaming installations, count verification timeout from the broadcast.
- startVerificationTimeoutCountdown(verificationId, streaming, response,
- verificationTimeout);
- }
+ final Intent requiredIntent;
+ final String receiverPermission;
+ if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.length == 1) {
+ // Prod code OR test code+single verifier.
+ requiredIntent = new Intent(verification);
+ if (!requiredVerifierPackagesOverridden) {
+ ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ requiredVerifierPackage, receivers.getList());
+ requiredIntent.setComponent(requiredVerifierComponent);
+ } else {
+ requiredIntent.setPackage(requiredVerifierPackage);
+ }
+ receiverPermission = android.Manifest.permission.PACKAGE_VERIFICATION_AGENT;
+ } else {
+ // Test code+multiple verifiers.
+ // Recreate the intent to contain test-only information.
+ requiredIntent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ requiredIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ requiredIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ requiredIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ requiredIntent.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+ PACKAGE_MIME_TYPE);
+ requiredIntent.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+ requiredIntent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+ if (rootHashString != null) {
+ requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH,
+ rootHashString);
+ }
+ requiredIntent.setPackage(requiredVerifierPackage);
+ // Negative verification id.
+ requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, -verificationId);
+ // OK not to have permission.
+ receiverPermission = null;
+ }
- mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!streaming) {
- // For NON-streaming installations, count verification timeout from
- // the broadcast was processed by all receivers.
- startVerificationTimeoutCountdown(verificationId, streaming, response,
- verificationTimeout);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(), requiredVerifierPackage,
+ verificationTimeout, verifierUserId, false, REASON_PACKAGE_VERIFIER,
+ "package verifier");
+
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, requiredUid);
+
+ if (streaming) {
+ // For streaming installations, count verification timeout from the broadcast.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
+
+ // Send the intent to the required verification agent, but only start the
+ // verification timeout after the target BroadcastReceivers have run.
+ mPm.mContext.sendOrderedBroadcastAsUser(requiredIntent, verifierUser,
+ receiverPermission, AppOpsManager.OP_NONE, options.toBundle(),
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!streaming) {
+ // For NON-streaming installations, count verification timeout from
+ // the broadcast was processed by all receivers.
+ startVerificationTimeoutCountdown(verificationId, streaming,
+ response, verificationTimeout);
+ }
}
- }
- }, null, 0, null, null);
+ }, null, 0, null, null);
+ }
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
@@ -617,7 +656,8 @@
*
* @return true if verification should be performed
*/
- private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId) {
+ private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
+ String[] requiredVerifierPackages) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
@@ -634,18 +674,21 @@
// only when not installed from ADB, skip verification for instant apps when
// the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mPm.mInstantAppInstallerActivity != null
- && mPm.mInstantAppInstallerActivity.packageName.equals(
- mPm.mRequiredVerifierPackage)) {
- try {
- mPm.mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
+ && mPm.mInstantAppInstallerActivity != null) {
+ String installerPackage = mPm.mInstantAppInstallerActivity.packageName;
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ if (installerPackage.equals(requiredVerifierPackage)) {
+ try {
+ mPm.mInjector.getSystemService(AppOpsManager.class)
+ .checkPackage(installerUid, requiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) {
}
- return false;
- } catch (SecurityException ignore) { }
+ }
}
}
return true;
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d49227d..905bcf9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -64,7 +64,7 @@
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
- ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_OTA);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
new file mode 100644
index 0000000..1497684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -0,0 +1,131 @@
+/*
+ * 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.pm.pkg.component;
+
+
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.SystemConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Utility methods for handling the tag {@code <install-constraints/>}
+ *
+ * @hide
+ */
+public class InstallConstraintsTagParser {
+
+ private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
+
+ /**
+ * @hide
+ */
+ public static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+ if (!allowlist.contains(pkg.getPackageName())) {
+ return input.skip("install-constraints cannot be used by this package");
+ }
+
+ ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+ if (prefixes.isSuccess()) {
+ if (validateFingerprintPrefixes(prefixes.getResult())) {
+ return input.success(pkg);
+ } else {
+ return input.skip(
+ "Install of this package is restricted on this device; device fingerprint"
+ + " does not start with one of the allowed prefixes");
+ }
+ }
+ return input.skip(prefixes.getErrorMessage());
+ }
+
+ private static ParseResult<Set<String>> parseFingerprintPrefixes(
+ ParseInput input, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> prefixes = new ArraySet<>();
+ int type;
+ while (true) {
+ // move to the tag that contains the next prefix
+ type = parser.next();
+ if (type == XmlPullParser.END_TAG) {
+ if (prefixes.size() == 0) {
+ return input.error("install-constraints must contain at least one constraint");
+ }
+ return input.success(prefixes);
+ } else if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+ ParseResult<String> parsedPrefix =
+ readFingerprintPrefixValue(input, res, parser);
+ if (parsedPrefix.isSuccess()) {
+ prefixes.add(parsedPrefix.getResult());
+ } else {
+ return input.error(parsedPrefix.getErrorMessage());
+ }
+ } else {
+ return input.error("Unexpected tag: " + parser.getName());
+ }
+
+ // consume the end tag of this attribute
+ type = parser.next();
+ if (type != XmlPullParser.END_TAG) {
+ return input.error("Expected end tag; instead got " + type);
+ }
+ }
+ }
+ }
+
+ private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
+ XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
+ try {
+ String value = sa.getString(
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
+ if (value == null) {
+ return input.error("Failed to specify prefix value");
+ }
+ return input.success(value);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
+ String fingerprint = Build.FINGERPRINT;
+ for (String prefix : prefixes) {
+ if (fingerprint.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index ee59810..8f953e7 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -293,6 +293,10 @@
pi.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
}
+ if (pkg.getImplicitPermissions().contains(pi.requestedPermissions[i])) {
+ pi.requestedPermissionsFlags[i] |=
+ PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index b60b9a0..5b8f473 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -94,6 +94,7 @@
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -168,9 +169,11 @@
public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
public static final String TAG_APPLICATION = "application";
+ public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
public static final String TAG_EAT_COMMENT = "eat-comment";
public static final String TAG_FEATURE_GROUP = "feature-group";
+ public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
public static final String TAG_INSTRUMENTATION = "instrumentation";
public static final String TAG_KEY_SETS = "key-sets";
public static final String TAG_MANIFEST = "manifest";
@@ -178,15 +181,16 @@
public static final String TAG_OVERLAY = "overlay";
public static final String TAG_PACKAGE = "package";
public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
- public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_PERMISSION = "permission";
public static final String TAG_PERMISSION_GROUP = "permission-group";
public static final String TAG_PERMISSION_TREE = "permission-tree";
+ public static final String TAG_PROFILEABLE = "profileable";
public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
public static final String TAG_QUERIES = "queries";
+ public static final String TAG_RECEIVER = "receiver";
public static final String TAG_RESTRICT_UPDATE = "restrict-update";
- public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_SUPPORTS_INPUT = "supports-input";
+ public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_USES_CONFIGURATION = "uses-configuration";
public static final String TAG_USES_FEATURE = "uses-feature";
public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
@@ -195,8 +199,6 @@
public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
- public static final String TAG_PROFILEABLE = "profileable";
- public static final String TAG_RECEIVER = "receiver";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -1026,6 +1028,8 @@
return input.success(pkg);
case TAG_RESTRICT_UPDATE:
return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+ case TAG_INSTALL_CONSTRAINTS:
+ return parseInstallConstraints(input, pkg, res, parser);
case TAG_QUERIES:
return parseQueries(input, pkg, res, parser);
default:
@@ -1719,6 +1723,12 @@
return input.success(pkg);
}
+ private static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+ }
+
private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
final int depth = parser.getDepth();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e062c38..abedd96 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -131,6 +131,7 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.power.ShutdownCheckPoints;
@@ -2095,6 +2096,49 @@
}
}
+ // Log activity starts which violate one of the following rules of the
+ // activity security model (ASM):
+ // 1. Only the top activity on a task can start activities on that task
+ // 2. Only the top activity on the top task can create new (top) tasks
+ // We don't currently block, but these checks may later become blocks
+ // TODO(b/236234252): Shift to BackgroundActivityStartController once
+ // class is ready
+ if (mSourceRecord != null) {
+ int callerUid = mSourceRecord.getUid();
+ ActivityRecord targetTopActivity =
+ targetTask != null ? targetTask.getTopNonFinishingActivity() : null;
+ boolean passesAsmChecks = newTask
+ ? mRootWindowContainer.hasResumedActivity(callerUid)
+ : targetTopActivity != null && targetTopActivity.getUid() == callerUid;
+
+ if (!passesAsmChecks) {
+ Slog.i(TAG, "Launching r: " + r
+ + " from background: " + mSourceRecord
+ + ". New task: " + newTask);
+ boolean newOrEmptyTask = newTask || (targetTopActivity == null);
+ FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+ /* caller_uid */
+ callerUid,
+ /* caller_activity_class_name */
+ mSourceRecord.info.name,
+ /* target_task_top_activity_uid */
+ newOrEmptyTask ? -1 : targetTopActivity.getUid(),
+ /* target_task_top_activity_class_name */
+ newOrEmptyTask ? null : targetTopActivity.info.name,
+ /* target_task_is_different */
+ newTask || !mSourceRecord.getTask().equals(targetTask),
+ /* target_activity_uid */
+ r.getUid(),
+ /* target_activity_class_name */
+ r.info.name,
+ /* target_intent_action */
+ r.intent.getAction(),
+ /* target_intent_flags */
+ r.intent.getFlags()
+ );
+ }
+ }
+
return START_SUCCESS;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 848e9a9..87a4fe9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@
* @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
* @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
* be visible in the recent task list in systemui
*/
- @Override
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ */
+ @Override
+ public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+ boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -2342,7 +2351,7 @@
final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
mRootWindowContainer.getRunningTasks(
- maxNum, list, flags, callingUid, callingProfileIds);
+ maxNum, list, flags, callingUid, callingProfileIds, displayId);
}
return list;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 345ec11..be995a8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -886,11 +886,12 @@
logIfTransactionTooLarge(r.intent, r.getSavedState());
- if (r.isEmbedded()) {
+ final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+ if (organizedTaskFragment != null) {
// Sending TaskFragmentInfo to client to ensure the info is updated before
// the activity creation.
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
- r.getOrganizedTaskFragment());
+ organizedTaskFragment);
}
// Create activity launch transaction.
@@ -1874,6 +1875,9 @@
// End power mode launch before going sleep
mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
+ // Rank task layers to make sure the {@link Task#mLayerRank} is updated.
+ mRootWindowContainer.rankTaskLayers();
+
removeSleepTimeouts();
if (mGoingToSleepWakeLock.isHeld()) {
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 2ea043a..6f19450 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.GameManagerInternal;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -48,6 +49,7 @@
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -64,6 +66,7 @@
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private final ActivityTaskManagerService mService;
+ private GameManagerInternal mGameManager;
private final AtomicFile mFile;
// Compatibility state: no longer ask user to select the mode.
@@ -375,6 +378,18 @@
float getCompatScale(String packageName, int uid) {
final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ if (mGameManager == null) {
+ mGameManager = LocalServices.getService(GameManagerInternal.class);
+ }
+ if (mGameManager != null) {
+ final int userId = userHandle.getIdentifier();
+ final float scalingFactor = mGameManager.getResolutionScalingFactor(packageName,
+ userId);
+ if (scalingFactor > 0) {
+ return 1f / scalingFactor;
+ }
+ }
+
if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) {
if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
return 1f / 0.9f;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3e2d7c9..7a9e6a9 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -398,16 +398,18 @@
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- if (!stateCopied) {
- state = new InsetsState(state);
- stateCopied = true;
+ InsetsState newState = new InsetsState();
+
+ // Only caption and IME are needed.
+ if (state.peekSource(ITYPE_CAPTION_BAR) != null) {
+ newState.addSource(state.peekSource(ITYPE_CAPTION_BAR));
}
- state.removeSource(ITYPE_STATUS_BAR);
- state.removeSource(ITYPE_NAVIGATION_BAR);
- state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
- if (windowingMode == WINDOWING_MODE_PINNED) {
- state.removeSource(ITYPE_IME);
+ if (windowingMode != WINDOWING_MODE_PINNED && state.peekSource(ITYPE_IME) != null) {
+ newState.addSource(state.peekSource(ITYPE_IME));
}
+
+ state = newState;
+ stateCopied = true;
}
return state;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8c3795..552c6a5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1820,6 +1820,10 @@
return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
}
+ boolean hasResumedActivity(int uid) {
+ return forAllActivities(ar -> ar.isState(RESUMED) && ar.getUid() == uid);
+ }
+
boolean isTopDisplayFocusedRootTask(Task task) {
return task != null && task == getTopDisplayFocusedRootTask();
}
@@ -3326,9 +3330,16 @@
@VisibleForTesting
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
- int flags, int callingUid, ArraySet<Integer> profileIds) {
- mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
- profileIds);
+ int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+ WindowContainer root = this;
+ if (displayId != INVALID_DISPLAY) {
+ root = getDisplayContent(displayId);
+ if (root == null) {
+ return;
+ }
+ }
+ mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+ root, callingUid, profileIds);
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191e..120fec0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -76,7 +76,7 @@
mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
mFilterOnlyVisibleRecents =
(flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
- mRecentTasks = root.mService.getRecentTasks();
+ mRecentTasks = recentTasks;
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e65f0bb..8de556a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1109,6 +1109,9 @@
// as the one in the task because either one of them could be the alias activity.
if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
intent.setComponent(this.intent.getComponent());
+ // Make sure the package name the same to prevent one of the intent is set while the
+ // other one is not.
+ intent.setPackage(this.intent.getPackage());
}
return intent.filterEquals(this.intent);
}
@@ -5319,7 +5322,23 @@
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
(destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+ boolean abort;
+ try {
+ abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
+ parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
+ callingUid, srec.info.packageName, null /* callingFeatureId */,
+ false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
+ null /* resultRecord */, null /* resultRootTask */);
+ } catch (SecurityException e) {
+ abort = true;
+ }
+ if (abort) {
+ Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+ foundParentInTask = false;
+ } else {
+ parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
+ srec.packageName);
+ }
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 589618e..e6afdd6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -586,7 +586,10 @@
}
// Cannot embed activity across TaskFragments for activity result.
- if (a.resultTo != null && a.resultTo.getTaskFragment() != this) {
+ // If the activity that started for result is finishing, it's likely that this start mode
+ // is used to place an activity in the same task. Since the finishing activity won't be
+ // able to get the results, so it's OK to embed in a different TaskFragment.
+ if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 602579f..6ca5648 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -16,12 +16,21 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,18 +41,21 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -63,10 +75,12 @@
private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
new ArrayMap<>();
/**
- * A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
+ * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
*/
- private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
- new ArrayList<>();
+ private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
+ new ArrayMap<>();
+
+ private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm;
@@ -82,10 +96,30 @@
private final ITaskFragmentOrganizer mOrganizer;
private final int mOrganizerPid;
private final int mOrganizerUid;
+
+ /**
+ * Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
+ * organizer.
+ */
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
new WeakHashMap<>();
- private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
- new WeakHashMap<>();
+
+ /**
+ * Map from {@link TaskFragment} to its leaf {@link Task#mTaskId}. Embedded
+ * {@link TaskFragment} will not be reparented until it is removed.
+ */
+ private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
+
+ /**
+ * Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
+ * organizer.
+ */
+ private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
+ new SparseArray<>();
+
+ /**
+ * Map from temporary activity token to the corresponding {@link ActivityRecord}.
+ */
private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
new WeakHashMap<>();
@@ -145,107 +179,120 @@
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
- void onTaskFragmentAppeared(TaskFragment tf) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- try {
- mOrganizer.onTaskFragmentAppeared(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- tf.mTaskFragmentAppearedSent = true;
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
- }
- onTaskFragmentParentInfoChanged(tf);
+ final int taskId = tf.getTask().mTaskId;
+ tf.mTaskFragmentAppearedSent = true;
+ mLastSentTaskFragmentInfos.put(tf, info);
+ mTaskFragmentTaskIds.put(tf, taskId);
+ return new TaskFragmentTransaction.Change(
+ TYPE_TASK_FRAGMENT_APPEARED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info)
+ .setTaskId(taskId);
}
- void onTaskFragmentVanished(TaskFragment tf) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
- try {
- mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
- }
tf.mTaskFragmentAppearedSent = false;
mLastSentTaskFragmentInfos.remove(tf);
- mLastSentTaskFragmentParentConfigs.remove(tf);
+
+ // Cleanup TaskFragmentParentConfig if this is the last TaskFragment in the Task.
+ final int taskId;
+ if (mTaskFragmentTaskIds.containsKey(tf)) {
+ taskId = mTaskFragmentTaskIds.remove(tf);
+ if (!mTaskFragmentTaskIds.containsValue(taskId)) {
+ // No more TaskFragment in the Task.
+ mLastSentTaskFragmentParentConfigs.remove(taskId);
+ }
+ } else {
+ // This can happen if the appeared wasn't sent before remove.
+ taskId = INVALID_TASK_ID;
+ }
+
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(tf.getTaskFragmentInfo())
+ .setTaskId(taskId);
}
- void onTaskFragmentInfoChanged(TaskFragment tf) {
- // Parent config may have changed. The controller will check if there is any important
- // config change for the organizer.
- onTaskFragmentParentInfoChanged(tf);
-
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
+ @NonNull TaskFragment tf) {
// Check if the info is different from the last reported info.
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
info.getConfiguration(), lastInfo.getConfiguration())) {
- return;
+ return null;
}
+
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
tf.getName());
- try {
- mOrganizer.onTaskFragmentInfoChanged(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
- }
+ mLastSentTaskFragmentInfos.put(tf, info);
+ return new TaskFragmentTransaction.Change(
+ TYPE_TASK_FRAGMENT_INFO_CHANGED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info)
+ .setTaskId(tf.getTask().mTaskId);
}
- void onTaskFragmentParentInfoChanged(TaskFragment tf) {
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
+ @NonNull Task task) {
+ final int taskId = task.mTaskId;
// Check if the parent info is different from the last reported parent info.
- if (tf.getParent() == null || tf.getParent().asTask() == null) {
- mLastSentTaskFragmentParentConfigs.remove(tf);
- return;
- }
- final Task parent = tf.getParent().asTask();
- final Configuration parentConfig = parent.getConfiguration();
- final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
- if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
- && parentConfig.windowConfiguration.getWindowingMode()
+ final Configuration taskConfig = task.getConfiguration();
+ final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
+ if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
+ && taskConfig.windowConfiguration.getWindowingMode()
== lastParentConfig.windowConfiguration.getWindowingMode()) {
- return;
+ return null;
}
+
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
- tf.getName(), parent.mTaskId);
- try {
- mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
- mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
- }
+ task.getName(), taskId);
+ mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
+ .setTaskId(taskId)
+ .setTaskConfiguration(taskConfig);
}
- void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
- int opType, Throwable exception) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentError(
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Sending TaskFragment error exception=%s", exception.toString());
final TaskFragmentInfo info =
taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
- try {
- mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
+ .setErrorCallbackToken(errorCallbackToken)
+ .setErrorBundle(errorBundle);
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ @Nullable
+ TaskFragmentTransaction.Change prepareActivityReparentToTask(
+ @NonNull ActivityRecord activity) {
if (activity.finishing) {
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
- return;
+ return null;
}
final Task task = activity.getTask();
if (task == null || task.effectiveUid != mOrganizerUid) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not in a task belong to the organizer app.");
- return;
+ return null;
}
if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not allowed to be embedded.");
- return;
+ return null;
}
final IBinder activityToken;
@@ -268,11 +315,10 @@
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
activity.token, task.mTaskId);
- try {
- mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
+ .setTaskId(task.mTaskId)
+ .setActivityIntent(activity.intent)
+ .setActivityToken(activityToken);
}
}
@@ -303,6 +349,7 @@
}
mTaskFragmentOrganizerState.put(organizer.asBinder(),
new TaskFragmentOrganizerState(organizer, pid, uid));
+ mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
}
}
@@ -375,7 +422,7 @@
*/
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
- ITaskFragmentOrganizer organizer, int taskId) {
+ @NonNull ITaskFragmentOrganizer organizer, int taskId) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +432,18 @@
}
}
- int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
+ int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
return state.mOrganizerUid;
}
- void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
+ if (taskFragment.getTask() == null) {
+ Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
+ + taskFragment);
+ return;
+ }
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {
return;
@@ -398,27 +451,15 @@
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_APPEARED);
if (pendingEvent == null) {
- pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
.setTaskFragment(taskFragment)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
}
}
- void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
- handleTaskFragmentInfoChanged(organizer, taskFragment,
- PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
- }
-
- void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment) {
- handleTaskFragmentInfoChanged(organizer, taskFragment,
- PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
- }
-
- private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment, int eventType) {
+ void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared.
@@ -426,64 +467,61 @@
}
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
if (pendingEvent == null) {
- pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer)
- .setTaskFragment(taskFragment)
- .build();
+ pendingEvent = new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_INFO_CHANGED, organizer)
+ .setTaskFragment(taskFragment)
+ .build();
} else {
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
// Skipped the info changed event if vanished event is pending.
return;
}
// Remove and add for re-ordering.
- mPendingTaskFragmentEvents.remove(pendingEvent);
+ removePendingEvent(pendingEvent);
// Reset the defer time when TaskFragment is changed, so that it can check again if
// the event should be sent to the organizer, for example the TaskFragment may become
// empty.
pendingEvent.mDeferTime = 0;
}
- mPendingTaskFragmentEvents.add(pendingEvent);
+ addPendingEvent(pendingEvent);
}
- void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (taskFragment == entry.mTaskFragment) {
- mPendingTaskFragmentEvents.remove(i);
- if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
- // If taskFragment appeared callback is pending, ignore the vanished request.
- return;
- }
+ final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ // Remove any pending events since this TaskFragment is being removed.
+ for (int i = pendingEvents.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = pendingEvents.get(i);
+ if (taskFragment == event.mTaskFragment) {
+ pendingEvents.remove(i);
}
}
- if (!taskFragment.mTaskFragmentAppearedSent) {
- return;
- }
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
.setTaskFragment(taskFragment)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
state.removeTaskFragment(taskFragment);
}
- void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
- TaskFragment taskFragment, int opType, Throwable exception) {
+ void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
validateAndGetState(organizer);
Slog.w(TAG, "onTaskFragmentError ", exception);
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ERROR, organizer)
.setErrorCallbackToken(errorCallbackToken)
.setTaskFragment(taskFragment)
.setException(exception)
.setOpType(opType)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
// Make sure the error event will be dispatched if there are no other changes.
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ void onActivityReparentToTask(@NonNull ActivityRecord activity) {
final ITaskFragmentOrganizer organizer;
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
// If the activity is previously embedded in an organized TaskFragment.
@@ -508,28 +546,30 @@
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
return;
}
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
.setActivity(activity)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
}
- boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
+ private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
+ mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
+ }
+
+ private void removePendingEvent(@NonNull PendingTaskFragmentEvent event) {
+ mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
+ }
+
+ boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
- private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
// remove all of the children of the organized TaskFragment
state.dispose();
// Remove any pending event of this organizer.
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
- if (event.mTaskFragmentOrg.asBinder().equals(organizer.asBinder())) {
- mPendingTaskFragmentEvents.remove(i);
- }
- }
+ mPendingTaskFragmentEvents.remove(organizer.asBinder());
mTaskFragmentOrganizerState.remove(organizer.asBinder());
}
@@ -539,7 +579,9 @@
* we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
* {@link TaskFragment} after the organizer process died.
*/
- private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+ @NonNull
+ private TaskFragmentOrganizerState validateAndGetState(
+ @NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (state == null) {
@@ -583,6 +625,8 @@
private final Throwable mException;
@Nullable
private final ActivityRecord mActivity;
+ @Nullable
+ private final Task mTask;
// Set when the event is deferred due to the host task is invisible. The defer time will
// be the last active time of the host task.
private long mDeferTime;
@@ -594,6 +638,7 @@
@Nullable IBinder errorCallbackToken,
@Nullable Throwable exception,
@Nullable ActivityRecord activity,
+ @Nullable Task task,
int opType) {
mEventType = eventType;
mTaskFragmentOrg = taskFragmentOrg;
@@ -601,6 +646,7 @@
mErrorCallbackToken = errorCallbackToken;
mException = exception;
mActivity = activity;
+ mTask = task;
mOpType = opType;
}
@@ -632,11 +678,13 @@
private Throwable mException;
@Nullable
private ActivityRecord mActivity;
+ @Nullable
+ private Task mTask;
private int mOpType;
- Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
+ Builder(@EventType int eventType, @NonNull ITaskFragmentOrganizer taskFragmentOrg) {
mEventType = eventType;
- mTaskFragmentOrg = taskFragmentOrg;
+ mTaskFragmentOrg = requireNonNull(taskFragmentOrg);
}
Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
@@ -649,13 +697,18 @@
return this;
}
- Builder setException(@Nullable Throwable exception) {
- mException = exception;
+ Builder setException(@NonNull Throwable exception) {
+ mException = requireNonNull(exception);
return this;
}
- Builder setActivity(@Nullable ActivityRecord activity) {
- mActivity = activity;
+ Builder setActivity(@NonNull ActivityRecord activity) {
+ mActivity = requireNonNull(activity);
+ return this;
+ }
+
+ Builder setTask(@NonNull Task task) {
+ mTask = requireNonNull(task);
return this;
}
@@ -666,29 +719,35 @@
PendingTaskFragmentEvent build() {
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
- mErrorCallbackToken, mException, mActivity, mOpType);
+ mErrorCallbackToken, mException, mActivity, mTask, mOpType);
}
}
}
@Nullable
- private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
- return entry;
+ private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
+ final ITaskFragmentOrganizer organizer = tf.getTaskFragmentOrganizer();
+ final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = events.get(i);
+ if (tf == event.mTaskFragment && event.isLifecycleEvent()) {
+ return event;
}
}
return null;
}
@Nullable
- private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+ private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
int type) {
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
- return entry;
+ final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+ final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = events.get(i);
+ if (taskFragment == event.mTaskFragment && type == event.mEventType) {
+ return event;
}
}
return null;
@@ -714,12 +773,25 @@
|| mPendingTaskFragmentEvents.isEmpty()) {
return;
}
+ final int organizerNum = mPendingTaskFragmentEvents.size();
+ for (int i = 0; i < organizerNum; i++) {
+ final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
+ mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
+ dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
+ }
+ }
+
+ void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull List<PendingTaskFragmentEvent> pendingEvents) {
+ if (pendingEvents.isEmpty()) {
+ return;
+ }
final ArrayList<Task> visibleTasks = new ArrayList<>();
final ArrayList<Task> invisibleTasks = new ArrayList<>();
final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
- for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
- final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+ for (int i = 0, n = pendingEvents.size(); i < n; i++) {
+ final PendingTaskFragmentEvent event = pendingEvents.get(i);
final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
if (task != null && (task.lastActiveTime <= event.mDeferTime
|| !(isTaskVisible(task, visibleTasks, invisibleTasks)
@@ -731,16 +803,35 @@
candidateEvents.add(event);
}
final int numEvents = candidateEvents.size();
+ if (numEvents == 0) {
+ return;
+ }
+
+ mTmpTaskSet.clear();
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
for (int i = 0; i < numEvents; i++) {
- dispatchEvent(candidateEvents.get(i));
+ final PendingTaskFragmentEvent event = candidateEvents.get(i);
+ if (event.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED
+ || event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED) {
+ final Task task = event.mTaskFragment.getTask();
+ if (mTmpTaskSet.add(task)) {
+ // Make sure the organizer know about the Task config.
+ transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ .setTask(task)
+ .build()));
+ }
+ }
+ transaction.addChange(prepareChange(event));
}
- if (numEvents > 0) {
- mPendingTaskFragmentEvents.removeAll(candidateEvents);
- }
+ mTmpTaskSet.clear();
+ dispatchTransactionInfo(organizer, transaction);
+ pendingEvents.removeAll(candidateEvents);
}
- private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
- ArrayList<Task> knownInvisibleTasks) {
+ private static boolean isTaskVisible(@NonNull Task task,
+ @NonNull ArrayList<Task> knownVisibleTasks,
+ @NonNull ArrayList<Task> knownInvisibleTasks) {
if (knownVisibleTasks.contains(task)) {
return true;
}
@@ -756,44 +847,63 @@
}
}
- void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
- PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+ void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
+ final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
if (event == null) {
return;
}
- dispatchEvent(event);
- mPendingTaskFragmentEvents.remove(event);
+ final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+ // Make sure the organizer know about the Task config.
+ transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ .setTask(taskFragment.getTask())
+ .build()));
+ transaction.addChange(prepareChange(event));
+ dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
+ mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
}
- private void dispatchEvent(PendingTaskFragmentEvent event) {
+ private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragmentTransaction transaction) {
+ if (transaction.isEmpty()) {
+ return;
+ }
+ try {
+ organizer.onTransactionReady(transaction);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+ }
+ }
+
+ @Nullable
+ private TaskFragmentTransaction.Change prepareChange(
+ @NonNull PendingTaskFragmentEvent event) {
final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
final TaskFragment taskFragment = event.mTaskFragment;
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
if (state == null) {
- return;
+ return null;
}
switch (event.mEventType) {
case PendingTaskFragmentEvent.EVENT_APPEARED:
- state.onTaskFragmentAppeared(taskFragment);
- break;
+ return state.prepareTaskFragmentAppeared(taskFragment);
case PendingTaskFragmentEvent.EVENT_VANISHED:
- state.onTaskFragmentVanished(taskFragment);
- break;
+ return state.prepareTaskFragmentVanished(taskFragment);
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
- state.onTaskFragmentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
- state.onTaskFragmentParentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentParentInfoChanged(event.mTask);
case PendingTaskFragmentEvent.EVENT_ERROR:
- state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
- event.mException);
- break;
+ return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
+ event.mOpType, event.mException);
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
- state.onActivityReparentToTask(event.mActivity);
+ return state.prepareActivityReparentToTask(event.mActivity);
+ default:
+ throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index adb8bf6..70dd9f3 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3389,6 +3389,13 @@
pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
}
+ if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+ pw.println(prefix + mLocalInsetsSourceProviders.size() + " LocalInsetsSourceProviders");
+ final String childPrefix = prefix + " ";
+ for (int i = 0; i < mLocalInsetsSourceProviders.size(); ++i) {
+ mLocalInsetsSourceProviders.valueAt(i).dump(pw, childPrefix);
+ }
+ }
}
final void updateSurfacePositionNonOrganized() {
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index af5718f..1d56078 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -29,8 +29,10 @@
#include <dirent.h>
#include <jni.h>
#include <linux/errno.h>
+#include <linux/time.h>
#include <log/log.h>
#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
#include <nativehelper/JNIHelp.h>
#include <processgroup/processgroup.h>
#include <stddef.h>
@@ -42,6 +44,7 @@
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -457,6 +460,12 @@
ATRACE_INSTANT_FOR_TRACK(ATRACE_COMPACTION_TRACK, "Cancel compaction");
}
+static jlong com_android_server_am_CachedAppOptimizer_threadCpuTimeNs(JNIEnv*, jobject) {
+ int64_t currentCpuTime = systemTime(CLOCK_THREAD_CPUTIME_ID);
+
+ return currentCpuTime;
+}
+
static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEnv*, jobject) {
struct sysinfo memoryInfo;
int error = sysinfo(&memoryInfo);
@@ -467,6 +476,16 @@
return (double)memoryInfo.freeswap / (double)memoryInfo.totalswap;
}
+static jlong com_android_server_am_CachedAppOptimizer_getUsedZramMemory() {
+ android::meminfo::SysMemInfo sysmeminfo;
+ return sysmeminfo.mem_zram_kb();
+}
+
+static jlong com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction() {
+ android::meminfo::SysMemInfo sysmeminfo;
+ return sysmeminfo.mem_compacted_kb("/sys/block/zram0/");
+}
+
static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
jint compactionFlags) {
compactProcessOrFallback(pid, compactionFlags);
@@ -520,8 +539,13 @@
/* name, signature, funcPtr */
{"cancelCompaction", "()V",
(void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
+ {"threadCpuTimeNs", "()J", (void*)com_android_server_am_CachedAppOptimizer_threadCpuTimeNs},
{"getFreeSwapPercent", "()D",
(void*)com_android_server_am_CachedAppOptimizer_getFreeSwapPercent},
+ {"getUsedZramMemory", "()J",
+ (void*)com_android_server_am_CachedAppOptimizer_getUsedZramMemory},
+ {"getMemoryFreedCompaction", "()J",
+ (void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
{"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 74ed3df..891bc0f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -462,10 +462,10 @@
mInputManager->getReader().dump(dump);
dump += "\n";
- mInputManager->getUnwantedInteractionBlocker().dump(dump);
+ mInputManager->getBlocker().dump(dump);
dump += "\n";
- mInputManager->getClassifier().dump(dump);
+ mInputManager->getProcessor().dump(dump);
dump += "\n";
mInputManager->getDispatcher().dump(dump);
@@ -702,7 +702,7 @@
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
ATRACE_CALL();
- mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
+ mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
@@ -1483,7 +1483,7 @@
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
+ mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
}
bool NativeInputManager::isPerDisplayTouchModeEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4cade3..c1c7bce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3552,11 +3552,6 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- if (mInjector.getPackageManagerInternal().filterAppAccess(packageName, caller.getUid(),
- userHandle)) {
- return false;
- }
-
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
diff --git a/services/proguard.flags b/services/proguard.flags
index c648f7d..c930346 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -64,6 +64,12 @@
# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
-keep public class com.android.server.utils.Slogf { *; }
+# Referenced in wear-service
+# HIDL interfaces
+-keep public class android.hidl.base.** { *; }
+-keep public class android.hidl.manager.** { *; }
+-keep public class com.android.server.wm.WindowManagerInternal { *; }
+
# Notification extractors
# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml.
-keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 45e0d09..4ef6875 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -628,6 +628,7 @@
CodePath.SAME, CodePath.DIFFERENT ->
throw AssertionError("secondDataPath cannot be a data path")
CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+ else -> {}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 2baa1ec..05cad16 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -17,12 +17,9 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-
import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
-
+import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -37,15 +34,18 @@
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.text.TextUtils;
-
import androidx.test.platform.app.InstrumentationRegistry;
-
import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
-
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -55,13 +55,6 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
/**
* Tests for {@link CachedAppOptimizer}.
*
@@ -147,36 +140,40 @@
@Test
public void init_setsDefaults() {
mCachedAppOptimizerUnderTest.init();
- assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
- CachedAppOptimizer.DEFAULT_USE_COMPACTION);
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
- CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
- CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
- assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
- assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+ }
Set<Integer> expected = new HashSet<>();
@@ -192,6 +189,7 @@
CachedAppOptimizer.DEFAULT_USE_FREEZER);
}
+ @SuppressWarnings("GuardedBy")
@Test
public void init_withDeviceConfigSetsParameters() {
// When the DeviceConfig already has a flag value stored (note this test will need to
@@ -256,11 +254,11 @@
assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(compactActionIntToAction(
(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(compactActionIntToAction(
(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
@@ -412,10 +410,12 @@
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
// Then the updates are reflected in the flags.
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(expectedSome));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(expectedFull));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(compactActionIntToAction(expectedSome));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(compactActionIntToAction(expectedFull));
+ }
}
}
@@ -431,11 +431,15 @@
CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- // Then the default values are reflected in the flag
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ // Then the default values are reflected in the flag
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ }
mCountDown = new CountDownLatch(2);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -444,10 +448,14 @@
CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ }
}
@Test
@@ -893,7 +901,9 @@
mProcessDependencies.setRss(rssBefore1);
mProcessDependencies.setRssAfterCompaction(rssAfter1); //
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -908,7 +918,9 @@
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction.
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS NOT compacted - values after compaction for process 1 should remain the
// same as from the last compaction.
@@ -924,7 +936,9 @@
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted - values after compaction for process 1 should be updated.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -965,7 +979,9 @@
mProcessDependencies.setRss(rssBelowThreshold);
mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -974,7 +990,9 @@
mProcessDependencies.setRss(rssAboveThreshold);
mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1008,22 +1026,16 @@
mProcessDependencies.setRss(rssBefore);
mProcessDependencies.setRssAfterCompaction(rssAfter);
- // Compaction should occur if (setAdj < min for process || setAdj > max for process) &&
- // (MIN < curAdj < MAX)
- // GIVEN OomAdj score below threshold.
- processRecord.mState.setSetAdj(899);
- processRecord.mState.setCurAdj(970);
- // WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ // When moving within cached state
+ mCachedAppOptimizerUnderTest.onOomAdjustChanged(
+ ProcessList.CACHED_APP_MIN_ADJ, ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
- // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj < MAX)
- processRecord.mState.setSetAdj(910);
- processRecord.mState.setCurAdj(930);
- // WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ // When moving into cached state
+ mCachedAppOptimizerUnderTest.onOomAdjustChanged(ProcessList.CACHED_APP_MIN_ADJ - 1,
+ ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1057,13 +1069,17 @@
processRecord.mState.setCurAdj(100);
// Compact process full
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// the process is not compacted
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
// Compact process some
- mCachedAppOptimizerUnderTest.compactAppSome(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// the process is not compacted
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -1072,7 +1088,9 @@
processRecord.mState.setCurAdj(100);
// We force a full compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, true);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ true);
waitForHandler();
// then process is compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1080,13 +1098,13 @@
mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
// We force a some compaction
- mCachedAppOptimizerUnderTest.compactAppSome(processRecord, true);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP, true);
waitForHandler();
// then process is compacted.
- String executedCompactAction =
- compactActionIntToString(processRecord.mOptRecord.getLastCompactAction());
- assertThat(executedCompactAction)
- .isEqualTo(mCachedAppOptimizerUnderTest.mCompactActionSome);
+ CachedAppOptimizer.CompactProfile executedCompactProfile =
+ processRecord.mOptRecord.getLastCompactProfile();
+ assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
}
private void setFlag(String key, String value, boolean defaultValue) throws Exception {
@@ -1162,7 +1180,8 @@
}
@Override
- public void performCompaction(String action, int pid) throws IOException {
+ public void performCompaction(CachedAppOptimizer.CompactAction action, int pid)
+ throws IOException {
mRss = mRssAfterCompaction;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 8d59dce..598c6b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -40,6 +40,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_ACTIVITY;
import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
@@ -146,6 +147,7 @@
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
+ @SuppressWarnings("GuardedBy")
@BeforeClass
public static void setUpOnce() {
sContext = getInstrumentation().getTargetContext();
@@ -196,7 +198,7 @@
new ActivityManagerService.Injector(sContext));
doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
- doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
+ doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index d675b0a..b53a2c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -201,7 +201,6 @@
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.disableCompatScale(mPackageName);
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
@@ -450,13 +449,13 @@
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
mockModifyGameModeGranted();
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
// We need to make sure the mode is supported before setting it.
mockDeviceConfigAll();
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
@@ -534,8 +533,8 @@
startUser(gameManagerService, USER_ID_1);
startUser(gameManagerService, USER_ID_2);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
- gameManagerService.updateConfigsForUser(USER_ID_2, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_2, true, mPackageName);
// Set User 1 to Standard
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -563,7 +562,7 @@
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
ArraySet<Integer> reportedModes = new ArraySet<>();
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
@@ -578,20 +577,20 @@
}
private void checkDownscaling(GameManagerService gameManagerService,
- int gameMode, String scaling) {
+ int gameMode, float scaling) {
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
- assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
+ assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
}
private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
boolean angleEnabled) {
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
@@ -604,7 +603,7 @@
private void checkLoadingBoost(GameManagerService gameManagerService, int gameMode,
int loadingBoost) {
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
@@ -621,7 +620,7 @@
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
@@ -715,7 +714,7 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
}
@@ -735,7 +734,7 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -757,9 +756,9 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -782,7 +781,7 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
}
@@ -805,7 +804,7 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -829,9 +828,9 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -858,9 +857,9 @@
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -933,7 +932,7 @@
public void testInterventionAllowScalingDefault() throws Exception {
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -944,7 +943,7 @@
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleFalse();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, -1.0f);
}
/**
@@ -956,7 +955,7 @@
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleTrue();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -1091,7 +1090,7 @@
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
@@ -1109,7 +1108,7 @@
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1126,7 +1125,7 @@
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1143,7 +1142,7 @@
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1404,4 +1403,102 @@
assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
}
+
+ @Test
+ public void testUpdateResolutionScalingFactor() {
+ mockModifyGameModeGranted();
+ mockDeviceConfigBattery();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ scalingFactor = 0.321f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_noDeviceConfig() {
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ scalingFactor = 0.321f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY,
+ USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_permissionDenied() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ assertThrows(SecurityException.class, () -> {
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ });
+ mockModifyGameModeGranted();
+ assertEquals(0.7f, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_noUserId() {
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_2);
+ final float scalingFactor = 0.123f;
+ assertThrows(IllegalArgumentException.class, () -> {
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ });
+ }
+
+ @Test
+ public void testGetResolutionScalingFactor_permissionDenied() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ assertThrows(SecurityException.class, () -> {
+ gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1);
+ });
+ }
+
+ @Test
+ public void testGetResolutionScalingFactor_noUserId() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_2);
+ assertEquals(-1f, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c..e4f9eaf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@
}).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
mRunningTaskInfos = new ArrayList<>();
- when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
- mRunningTaskInfos);
+ when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+ .thenReturn(mRunningTaskInfos);
final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 99693d2..7111047 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -35,6 +35,7 @@
import android.os.HandlerThread;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;
@@ -97,9 +98,10 @@
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
for(int i = 0; i < uidStates.size(); i++) {
final AppOpsService.UidState uidState = uidStates.valueAt(i);
- if (uidState.opModes != null) {
- final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
- final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null) {
+ final int uidMode1 = opModes.get(op1, defaultModeOp1);
+ final int uidMode2 = opModes.get(op2, defaultModeOp2);
assertEquals(uidMode1, uidMode2);
if (uidMode1 != defaultModeOp1) {
numberOfNonDefaultOps++;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
new file mode 100644
index 0000000..6a18dc1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerControllerTest {
+ private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+ private static final int DISPLAY_ID = 42;
+
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private DisplayPowerController.Injector mInjector;
+ private Context mContextSpy;
+
+ @Mock
+ private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+ @Mock
+ private SensorManager mSensorManagerMock;
+ @Mock
+ private DisplayBlanker mDisplayBlankerMock;
+ @Mock
+ private LogicalDisplay mLogicalDisplayMock;
+ @Mock
+ private DisplayDevice mDisplayDeviceMock;
+ @Mock
+ private BrightnessTracker mBrightnessTrackerMock;
+ @Mock
+ private BrightnessSetting mBrightnessSettingMock;
+ @Mock
+ private WindowManagerPolicy mWindowManagerPolicyMock;
+ @Mock
+ private PowerManager mPowerManagerMock;
+ @Mock
+ private Resources mResourcesMock;
+ @Mock
+ private DisplayDeviceConfig mDisplayDeviceConfigMock;
+ @Mock
+ private DisplayPowerState mDisplayPowerStateMock;
+ @Mock
+ private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper());
+ mInjector = new DisplayPowerController.Injector() {
+ @Override
+ DisplayPowerController.Clock getClock() {
+ return mClock::now;
+ }
+
+ @Override
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return mDisplayPowerStateMock;
+ }
+
+ @Override
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return mDualRampAnimatorMock;
+ }
+ };
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ @Test
+ public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ Sensor proxSensor = setUpProxSensor();
+
+ DisplayPowerController dpc = new DisplayPowerController(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ });
+
+ when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+ // send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState to start listener for the prox sensor
+ advanceTime(1);
+
+ SensorEventListener listener = getSensorEventListener(proxSensor);
+ assertNotNull(listener);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+ dpc.stop();
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ private Sensor setUpProxSensor() throws Exception {
+ Sensor proxSensor = TestUtils.createSensor(
+ Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+ .thenReturn(List.of(proxSensor));
+ return proxSensor;
+ }
+
+ private SensorEventListener getSensorEventListener(Sensor sensor) {
+ verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+ eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+ return mSensorEventListenerCaptor.getValue();
+ }
+
+ private void setUpDisplay(int displayId, String uniqueId) {
+ DisplayInfo info = new DisplayInfo();
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+ when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+ when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+ when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+ when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+ when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+ when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+ when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+ when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = Sensor.STRING_TYPE_PROXIMITY;
+ name = null;
+ }
+ });
+ when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
new file mode 100644
index 0000000..2165301
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.pm
+
+import android.os.Build
+import android.os.SystemProperties
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class InitAppsHelperTest {
+ @Rule
+ @JvmField
+ val rule = MockSystemRule()
+
+ @Before
+ fun setUp() {
+ rule.system().stageNominalSystemState()
+ }
+
+ private fun mockNoFirstBoot() {
+ // To mock that this is not the first boot
+ rule.system().stageScanExistingPackage("a.data.package", 1L,
+ rule.system().dataAppDirectory)
+ }
+
+ private fun mockFingerprintChanged() {
+ val versionInfo = Settings.VersionInfo()
+ versionInfo.fingerprint = "mockFingerprintForTesting"
+ whenever(rule.mocks().settings.internalVersion) {
+ versionInfo
+ }
+ }
+
+ private fun mockFingerprintUnchanged() {
+ // To mock that this is not the first boot
+ val versionInfo = Settings.VersionInfo()
+ versionInfo.fingerprint = MockSystem.DEFAULT_VERSION_INFO.fingerprint
+ whenever(rule.mocks().settings.internalVersion) {
+ versionInfo
+ }
+ }
+
+ private fun mockOta() {
+ wheneverStatic {
+ SystemProperties.getBoolean("persist.pm.mock-upgrade", false)
+ }.thenReturn(true)
+ }
+
+ private fun createPackageManagerService(): PackageManagerService {
+ return spy(PackageManagerService(rule.mocks().injector,
+ false /*factoryTest*/,
+ MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+ false /*isEngBuild*/,
+ false /*isUserDebugBuild*/,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ Build.VERSION.INCREMENTAL))
+ }
+
+ @Test
+ fun testSystemScanFlagOnFirstBoot() {
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(true)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+
+ @Test
+ fun testSystemScanFlagWithMockOTA() {
+ mockNoFirstBoot()
+ mockFingerprintUnchanged()
+ mockOta()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+
+ @Test
+ fun testSystemScanFlagNoOTA() {
+ mockNoFirstBoot()
+ mockFingerprintUnchanged()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(0)
+ }
+
+ @Test
+ fun testSystemScanFlagWithFingerprintChanged() {
+ mockNoFirstBoot()
+ mockFingerprintChanged()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a9ee84f..1921ce7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -135,8 +135,9 @@
java_library {
name: "servicestests-core-utils",
srcs: [
- "src/com/android/server/pm/PackageSettingBuilder.java",
"src/com/android/server/am/DeviceConfigSession.java",
+ "src/com/android/server/display/TestUtils.java",
+ "src/com/android/server/pm/PackageSettingBuilder.java",
],
static_libs: [
"services.core",
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 c80547c..966df4f 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
@@ -124,6 +124,8 @@
private VirtualDeviceImpl mDeviceImpl;
private InputController mInputController;
private AssociationInfo mAssociationInfo;
+ private VirtualDeviceManagerService mVdms;
+ private VirtualDeviceManagerInternal mLocalService;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
@@ -139,6 +141,8 @@
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
+ private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
+ @Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -215,6 +219,9 @@
mAssociationInfo = new AssociationInfo(1, 0, null,
MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
+ mVdms = new VirtualDeviceManagerService(mContext);
+ mLocalService = mVdms.getLocalServiceInstance();
+
VirtualDeviceParams params = new VirtualDeviceParams
.Builder()
.setBlockedActivities(getBlockedActivities())
@@ -235,6 +242,28 @@
}
@Test
+ public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+
+ mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+ }
+
+ @Test
+ public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+
+ mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+ }
+
+ @Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
index 094b7af..b044d7a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
@@ -42,7 +42,7 @@
ByteBuffer buffer = ByteBuffer.allocate(handle.length);
buffer.put(handle, 0, handle.length);
buffer.flip();
- int version = buffer.get();
+ buffer.get(); // version
sid = buffer.getLong();
password = new byte[buffer.remaining()];
buffer.get(password);
@@ -50,7 +50,7 @@
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
- buffer.put((byte)0);
+ buffer.put((byte)0); // version
buffer.putLong(sid);
buffer.put(password);
return buffer.array();
@@ -70,14 +70,14 @@
ByteBuffer buffer = ByteBuffer.allocate(handle.length);
buffer.put(handle, 0, handle.length);
buffer.flip();
- int version = buffer.get();
+ buffer.get(); // version
challenge = buffer.getLong();
sid = buffer.getLong();
}
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
- buffer.put((byte)0);
+ buffer.put((byte)0); // version
buffer.putLong(challenge);
buffer.putLong(sid);
return buffer.array();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
index 6de7fdd..ec708ad 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
@@ -250,8 +250,8 @@
// schedule (a) an alarm for non-strong biometric fallback timeout and (b) an alarm for
// non-strong biometric idle timeout, so later we can verify that unlocking with
// strong biometric or primary auth will cancel those alarms
- mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID);
- mStrongAuth.scheduleNonStrongBiometricIdleTimeout(PRIMARY_USER_ID);
+ mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, userId);
+ mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId);
}
private void verifyAlarmsCancelledAndNonStrongBiometricAllowed(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 186a04f..8cb18a8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -35,7 +35,6 @@
private FakeGateKeeperService mGateKeeper;
private IWeaver mWeaverService;
- private PasswordSlotManagerTestable mPasswordSlotManager;
public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
FakeGateKeeperService gatekeeper, UserManager userManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
index 0f24fb2..2faf6a2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
@@ -16,8 +16,9 @@
package com.android.server.locksettings;
+import static org.junit.Assert.assertEquals;
+
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +38,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class PasswordSlotManagerTests extends AndroidTestCase {
+public class PasswordSlotManagerTests {
PasswordSlotManagerTestable mManager;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b01c1c8..858f658 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -125,7 +125,6 @@
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
- private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -140,7 +139,6 @@
MockableRebootEscrowInjected injected) {
super(context, storage);
mRebootEscrow = rebootEscrow;
- mServiceConnection = null;
mServerBased = false;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@@ -161,7 +159,6 @@
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
super(context, storage);
- mServiceConnection = serviceConnection;
mRebootEscrow = null;
mServerBased = true;
RebootEscrowProviderServerBasedImpl.Injector injector =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index c2e83f2..ea5caa8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -678,8 +678,7 @@
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
- SecretKey applicationKey =
- addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
@@ -710,8 +709,7 @@
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
- SecretKey applicationKey =
- addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 3fd2c97..c546a74 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -587,7 +587,7 @@
return keyGenerator.generateKey();
}
- class PlatformKeyManagerTestable extends PlatformKeyManager {
+ static class PlatformKeyManagerTestable extends PlatformKeyManager {
private IGateKeeperService mGateKeeperService;
PlatformKeyManagerTestable(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index aceae61..281195d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -134,7 +134,6 @@
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final String TEST_ALIAS = "nick";
private static final String TEST_ALIAS2 = "bob";
- private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
private static final int APPLICATION_KEY_SIZE_BYTES = 32;
private static final int GENERATION_ID = 1;
private static final byte[] NONCE = getUtf8Bytes("nonce");
@@ -503,8 +502,6 @@
@Test
public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception {
- int uid = Binder.getCallingUid();
- int userId = UserHandle.getCallingUserId();
long certSerial = 1000L;
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
@@ -636,7 +633,6 @@
throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
- long certSerial = 1000L;
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 5d4be1b..4bf99e0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -39,7 +39,6 @@
private static final String KEY_ALGORITHM = "AES";
private static final long DEFAULT_SERIAL = 10001;
- private static final String CERT_PATH_ENCODING = "PkiPath";
private static final String CERT_PATH_1_BASE64 = ""
+ "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
index 0b15a12..1c9c6dc 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
@@ -21,11 +21,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -49,7 +47,6 @@
private static final long USER_SERIAL_NUMBER = 101L;
private static final long USER_SERIAL_NUMBER_2 = 202L;
- private Context mContext;
private CleanupManager mManager;
@Mock private RecoverableKeyStoreDb mDatabase;
@@ -60,8 +57,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getTargetContext();
- mManager = new CleanupManager(mContext, mRecoverySnapshotStorage, mDatabase, mUserManager,
+ mManager = new CleanupManager(mRecoverySnapshotStorage, mDatabase, mUserManager,
mApplicationKeyStorage);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
index 51498a6..a62569f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -23,7 +23,9 @@
@Presubmit
public class PackageVerificationStateTest extends AndroidTestCase {
- private static final int REQUIRED_UID = 1948;
+ private static final int REQUIRED_UID_1 = 1948;
+
+ private static final int REQUIRED_UID_2 = 1949;
private static final int SUFFICIENT_UID_1 = 1005;
@@ -31,12 +33,12 @@
public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -47,23 +49,111 @@
public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
- assertFalse("Installation should be marked as allowed",
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_AllowedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_DeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_FirstDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_SecondDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
state.isInstallAllowed());
}
public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -78,18 +168,46 @@
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
- assertFalse("Installation should be marked as allowed",
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_OneRequiredDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
state.isInstallAllowed());
}
public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -104,7 +222,7 @@
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -115,7 +233,7 @@
public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -131,7 +249,7 @@
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -142,7 +260,7 @@
public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -153,7 +271,7 @@
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -174,7 +292,7 @@
public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -185,7 +303,7 @@
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID,
+ state.setVerifierResponse(REQUIRED_UID_1,
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
assertTrue("Verification should be marked as complete immediately",
@@ -213,17 +331,17 @@
public void testAreAllVerificationsComplete_onlyVerificationPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertFalse(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
@@ -233,28 +351,28 @@
public void testAreAllVerificationsComplete_bothPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyVerificationFails() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
assertFalse(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index e9171c0c..92c7871 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -336,6 +336,59 @@
assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_successful() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist())
+ .containsExactly("com.android.apex1");
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noPackage() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed/>\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
+ * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noAppConfigs() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
@Test
public void readApexPrivAppPermissions_addAllPermissions()
throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
new file mode 100644
index 0000000..1f7b65e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
@@ -0,0 +1,95 @@
+/*
+ * 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.GameManagerInternal;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link CompatModePackages} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CompatModePackagesTests
+ */
+@SmallTest
+@Presubmit
+public class CompatModePackagesTests extends SystemServiceTestsBase {
+ ActivityTaskManagerService mAtm;
+ GameManagerInternal mGm;
+ static final String TEST_PACKAGE = "compat.mode.packages";
+ static final int TEST_USER_ID = 1;
+
+ @Before
+ public void setUp() {
+ mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
+ mGm = mock(GameManagerInternal.class);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(GameManagerInternal.class);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsPositive() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = 0.25f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1 / scale,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsZero() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = 0f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsNegative() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = -1f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_noGameManager() {
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+}
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 83f375f..adf694c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@
public boolean mLastAllowed;
@Override
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
- super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+ super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 68079f4..9e658e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -72,6 +72,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
@@ -169,7 +170,8 @@
public void testTaskLayerRank() {
final Task rootTask = new TaskBuilder(mSupervisor).build();
final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
- new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task1).build();
+ activity1.mVisibleRequested = true;
mWm.mRoot.rankTaskLayers();
assertEquals(1, task1.mLayerRank);
@@ -177,7 +179,8 @@
assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
final Task task2 = new TaskBuilder(mSupervisor).build();
- new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true;
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
+ activity2.mVisibleRequested = true;
mWm.mRoot.rankTaskLayers();
// Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
@@ -192,6 +195,17 @@
assertEquals(1, task1.mLayerRank);
assertEquals(2, task2.mLayerRank);
+
+ // The rank should be updated to invisible when device went to sleep.
+ activity1.mVisibleRequested = false;
+ activity2.mVisibleRequested = false;
+ doReturn(true).when(mAtm).isSleepingOrShuttingDownLocked();
+ doReturn(true).when(mRootWindowContainer).putTasksToSleep(anyBoolean(), anyBoolean());
+ mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+ doReturn(false).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
+ mAtm.mTaskSupervisor.checkReadyForSleepLocked(false /* allowDelay */);
+ assertEquals(Task.LAYER_RANK_INVISIBLE, task1.mLayerRank);
+ assertEquals(Task.LAYER_RANK_INVISIBLE, task2.mLayerRank);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b2366..b1acae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@
import androidx.test.filters.MediumTest;
+import com.google.common.truth.Correspondence;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@
public class RunningTasksTest extends WindowTestsBase {
private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+ private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+ Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
private RunningTasks mRunningTasks;
@@ -91,8 +96,8 @@
// collected from all tasks across all the stacks
final int numFetchTasks = 5;
ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < numFetchTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@
// and does not crash
tasks.clear();
mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numTasks);
for (int i = 0; i < numTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, fetchTasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(fetchTasks).hasSize(numFetchTasks);
assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@
task.intent = activity.intent;
return task;
}
+
+ @Test
+ public void testMultipleDisplays() {
+ final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final int numTasks = 10;
+ final ArrayList<Task> tasks = new ArrayList<>();
+ for (int i = 0; i < numTasks; i++) {
+ final Task stack = new TaskBuilder(mSupervisor)
+ .setDisplay(i % 2 == 0 ? display0 : display1)
+ .setOnTop(true)
+ .build();
+ final Task task = createTask(stack, ".Task" + i, i, i, null);
+ tasks.add(task);
+ }
+
+ final int numFetchTasks = numTasks;
+ final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 2, 4, 6, 8);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(1, 3, 5, 7, 9);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index eba2755..8b3cff8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -75,6 +75,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Build/Install/Run:
@@ -92,7 +94,6 @@
private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer;
private TaskFragment mTaskFragment;
- private TaskFragmentInfo mTaskFragmentInfo;
private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken;
@@ -100,14 +101,19 @@
private IBinder mErrorToken;
private Rect mTaskFragBounds;
+ @Mock
+ private TaskFragmentInfo mTaskFragmentInfo;
+ @Mock
+ private Task mTask;
+
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken();
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
- mTaskFragmentInfo = mock(TaskFragmentInfo.class);
mFragmentToken = new Binder();
mTaskFragment =
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
@@ -131,6 +137,8 @@
@Test
public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+ doReturn(mTask).when(mTaskFragment).getTask();
+
assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
@@ -140,16 +148,21 @@
assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
-
- assertThrows(IllegalArgumentException.class, () -> mController
- .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
- mTaskFragment));
}
@Test
public void testOnTaskFragmentAppeared() {
mController.registerOrganizer(mIOrganizer);
+ // No-op when the TaskFragment is not attached.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+
+ // Send callback when the TaskFragment is attached.
+ setupMockParent(mTaskFragment, mTask);
+
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -159,9 +172,21 @@
@Test
public void testOnTaskFragmentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
+ setupMockParent(mTaskFragment, mTask);
+
+ // No-op if onTaskFragmentAppeared is not called yet.
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+ // Call onTaskFragmentAppeared first.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
+ verify(mOrganizer).onTaskFragmentAppeared(any());
+
// No callback if the info is not changed.
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
@@ -194,47 +219,74 @@
}
@Test
+ public void testOnTaskFragmentVanished_clearUpRemaining() {
+ mController.registerOrganizer(mIOrganizer);
+ setupMockParent(mTaskFragment, mTask);
+
+ // Not trigger onTaskFragmentAppeared.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+ verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+
+ // Not trigger onTaskFragmentInfoChanged.
+ // Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+ clearInvocations(mOrganizer);
+ doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+ verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+ }
+
+ @Test
public void testOnTaskFragmentParentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
- final Task parent = mock(Task.class);
- final Configuration parentConfig = new Configuration();
- parentConfig.smallestScreenWidthDp = 10;
- doReturn(parent).when(mTaskFragment).getParent();
- doReturn(parentConfig).when(parent).getConfiguration();
- doReturn(parent).when(parent).asTask();
+ setupMockParent(mTaskFragment, mTask);
+ mTask.getConfiguration().smallestScreenWidthDp = 10;
- mTaskFragment.mTaskFragmentAppearedSent = true;
- mController.onTaskFragmentParentInfoChanged(
+ mController.onTaskFragmentAppeared(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// No extra callback if the info is not changed.
clearInvocations(mOrganizer);
- mController.onTaskFragmentParentInfoChanged(
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
// Trigger callback if the size is changed.
- parentConfig.smallestScreenWidthDp = 100;
- mController.onTaskFragmentParentInfoChanged(
+ mTask.getConfiguration().smallestScreenWidthDp = 100;
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// Trigger callback if the windowing mode is changed.
clearInvocations(mOrganizer);
- parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
- mController.onTaskFragmentParentInfoChanged(
+ mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
}
@Test
@@ -1089,4 +1141,15 @@
.put(mFragmentToken, mTaskFragment);
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
}
+
+ /** Setups the mock Task as the parent of the given TaskFragment. */
+ private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
+ doReturn(mockParent).when(taskFragment).getTask();
+ final Configuration taskConfig = new Configuration();
+ doReturn(taskConfig).when(mockParent).getConfiguration();
+
+ // Task needs to be visible
+ mockParent.lastActiveTime = 100;
+ doReturn(true).when(mockParent).shouldBeVisible(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 1096351..88eadfc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -32,6 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
@@ -468,6 +469,10 @@
newActivity.resultTo = activity;
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
newTaskFragment.isAllowedToEmbedActivity(newActivity));
+
+ // Allow embedding if the resultTo activity is finishing.
+ activity.finishing = true;
+ assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index f4323db..6a7e388 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -846,6 +846,7 @@
new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
final Intent intent = new Intent();
+ intent.setPackage(DEFAULT_COMPONENT_PACKAGE_NAME);
intent.setComponent(aliasComponent);
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
index 41f9fae..6fc4b67 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -31,15 +31,14 @@
public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
- if (UsbPortHidl.isServicePresent(null)) {
- logAndPrint(Log.INFO, null, "USB HAL HIDL present");
- return new UsbPortHidl(portManager, pw);
- }
if (UsbPortAidl.isServicePresent(null)) {
logAndPrint(Log.INFO, null, "USB HAL AIDL present");
return new UsbPortAidl(portManager, pw);
}
-
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
return null;
}
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bfa60ba..795ecf3 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1207,13 +1207,8 @@
/**
* Initialize the service state. Set everything to the default value.
- *
- * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
- * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
- * is on AP-assisted mode, where IWLAN should be reported through WLAN.
- * {@link NetworkRegistrationInfo}.
*/
- private void init(boolean legacyMode) {
+ private void init() {
if (DBG) Rlog.d(LOG_TAG, "init");
mVoiceRegState = STATE_OUT_OF_SERVICE;
mDataRegState = STATE_OUT_OF_SERVICE;
@@ -1245,13 +1240,11 @@
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
.build());
- if (!legacyMode) {
- addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
- .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
- .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
- .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
- .build());
- }
+ addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+ .build());
}
mOperatorAlphaLongRaw = null;
mOperatorAlphaShortRaw = null;
@@ -1260,11 +1253,11 @@
}
public void setStateOutOfService() {
- init(true);
+ init();
}
public void setStateOff() {
- init(true);
+ init();
mVoiceRegState = STATE_POWER_OFF;
mDataRegState = STATE_POWER_OFF;
}
@@ -1272,14 +1265,11 @@
/**
* Set the service state to out-of-service
*
- * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
- * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
- * is on AP-assisted mode, where IWLAN should be reported through WLAN.
* @param powerOff {@code true} if this is a power off case (i.e. Airplane mode on).
* @hide
*/
- public void setOutOfService(boolean legacyMode, boolean powerOff) {
- init(legacyMode);
+ public void setOutOfService(boolean powerOff) {
+ init();
if (powerOff) {
mVoiceRegState = STATE_POWER_OFF;
mDataRegState = STATE_POWER_OFF;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7468a9..7893992 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6641,7 +6641,7 @@
* list will be provided. If an error occurs, null will be provided unless the onError
* callback is overridden.
*
- * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
+ * @param cellInfo a list of {@link CellInfo} or an empty list.
*
* {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
*/
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 531dd7c..35f076f 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -6,6 +6,8 @@
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
+ method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
+ method public void clearCrossProfileIntentFilters(int);
method public int getInstallReason(String, android.os.UserHandle);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
method public String[] getNamesForUids(int[]);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index e0ae193..e138d33 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -108,7 +108,8 @@
}
/**
- * Checks that the [ComponentMatcher.TASK_BAR] layer is visible during the whole transition
+ * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the start and end of the
+ * transition
*
* Note: Large screen only
*/
@@ -132,7 +133,8 @@
}
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible during the whole transition
+ * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible at the start and end
+ * of the transition
*/
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index d173b72..5e21252 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -53,6 +53,16 @@
}
/**
+ * Checks that [ComponentMatcher.TASK_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
+fun FlickerTestParameter.taskBarWindowIsVisibleAtEnd() {
+ assertWmEnd {
+ this.isAboveAppWindowVisible(ComponentMatcher.TASK_BAR)
+ }
+}
+
+/**
* If [allStates] is true, checks if the stack space of all displays is fully covered
* by any visible layer, during the whole transitions
*
@@ -103,9 +113,25 @@
* trace
*/
fun FlickerTestParameter.taskBarLayerIsVisibleAtStartAndEnd() {
+ this.taskBarLayerIsVisibleAtStart()
+ this.taskBarLayerIsVisibleAtEnd()
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the start of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() {
assertLayersStart {
this.isVisible(ComponentMatcher.TASK_BAR)
}
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the end of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() {
assertLayersEnd {
this.isVisible(ComponentMatcher.TASK_BAR)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
index 2c2e860..3ff59e9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -52,6 +52,9 @@
get() = {
super.transition(this)
setup {
+ test{
+ tapl.setExpectedRotationCheckEnabled(false)
+ }
eachRun {
// 1. Open camera - cold -> close it first
cameraApp.exit(wmHelper)
@@ -76,10 +79,99 @@
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
- @Presubmit
+ @Postsubmit
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun focusChanges() = super.focusChanges()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 6e3e74d..866e819 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -32,8 +32,12 @@
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
+import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -231,6 +235,42 @@
super.appWindowBecomesTopWindow()
}
+ /**
+ * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the end of the transition
+ *
+ * Note: Large screen only
+ */
+ @Postsubmit
+ @Test
+ open fun taskBarWindowIsVisibleAtEnd() {
+ Assume.assumeFalse(testSpec.isTablet)
+ testSpec.taskBarWindowIsVisibleAtEnd()
+ }
+
+ /**
+ * Checks that the [ComponentMatcher.TASK_BAR] layer is visible at the end of the transition
+ *
+ * Note: Large screen only
+ */
+ @Postsubmit
+ @Test
+ open fun taskBarLayerIsVisibleAtEnd() {
+ Assume.assumeFalse(testSpec.isTablet)
+ testSpec.taskBarLayerIsVisibleAtEnd()
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is locked at the start")
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is locked at the start")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index cbf920ac..035f911 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -472,6 +472,7 @@
manifest_action["compatible-screens"]["screen"];
manifest_action["supports-gl-texture"];
manifest_action["restrict-update"];
+ manifest_action["install-constraints"]["fingerprint-prefix"];
manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
deleted file mode 100644
index 15088fc..0000000
--- a/tools/stringslint/stringslint.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
-
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Enforces common Android string best-practices. It ignores lint messages from
-a previous strings file, if provided.
-
-Usage: stringslint.py strings.xml
-Usage: stringslint.py strings.xml old_strings.xml
-
-In general:
-* Errors signal issues that must be fixed before submitting, and are only
- used when there are no false-positives.
-* Warnings signal issues that might need to be fixed, but need manual
- inspection due to risk of false-positives.
-* Info signal issues that should be fixed to match best-practices, such
- as providing comments to aid translation.
-"""
-
-import re, sys, codecs
-import lxml.etree as ET
-
-BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
-
-def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
- # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
- codes = []
- if reset: codes.append("0")
- else:
- if not fg is None: codes.append("3%d" % (fg))
- if not bg is None:
- if not bright: codes.append("4%d" % (bg))
- else: codes.append("10%d" % (bg))
- if bold: codes.append("1")
- elif dim: codes.append("2")
- else: codes.append("22")
- return "\033[%sm" % (";".join(codes))
-
-warnings = None
-
-def warn(tag, msg, actual, expected, color=YELLOW):
- global warnings
- key = "%s:%d" % (tag.attrib["name"], hash(msg))
- value = "%sLine %d: '%s':%s %s" % (format(fg=color, bold=True),
- tag.sourceline,
- tag.attrib["name"],
- format(reset=True),
- msg)
- if not actual is None: value += "\n\tActual: %s%s%s" % (format(dim=True),
- actual,
- format(reset=True))
- if not expected is None: value += "\n\tExample: %s%s%s" % (format(dim=True),
- expected,
- format(reset=True))
- warnings[key] = value
-
-
-def error(tag, msg, actual, expected):
- warn(tag, msg, actual, expected, RED)
-
-def info(tag, msg, actual, expected):
- warn(tag, msg, actual, expected, CYAN)
-
-# Escaping logic borrowed from https://stackoverflow.com/a/24519338
-ESCAPE_SEQUENCE_RE = re.compile(r'''
- ( \\U........ # 8-digit hex escapes
- | \\u.... # 4-digit hex escapes
- | \\x.. # 2-digit hex escapes
- | \\[0-7]{1,3} # Octal escapes
- | \\N\{[^}]+\} # Unicode characters by name
- | \\[\\'"abfnrtv] # Single-character escapes
- )''', re.UNICODE | re.VERBOSE)
-
-def decode_escapes(s):
- def decode_match(match):
- return codecs.decode(match.group(0), 'unicode-escape')
-
- s = re.sub(r"\n\s*", " ", s)
- s = ESCAPE_SEQUENCE_RE.sub(decode_match, s)
- s = re.sub(r"%(\d+\$)?[a-z]", "____", s)
- s = re.sub(r"\^\d+", "____", s)
- s = re.sub(r"<br/?>", "\n", s)
- s = re.sub(r"</?[a-z]+>", "", s)
- return s
-
-def sample_iter(tag):
- if not isinstance(tag, ET._Comment) and re.match("{.*xliff.*}g", tag.tag) and "example" in tag.attrib:
- yield tag.attrib["example"]
- elif tag.text:
- yield decode_escapes(tag.text)
- for e in tag:
- for v in sample_iter(e):
- yield v
- if e.tail:
- yield decode_escapes(e.tail)
-
-def lint(path):
- global warnings
- warnings = {}
-
- with open(path) as f:
- raw = f.read()
- if len(raw.strip()) == 0:
- return warnings
- tree = ET.fromstring(bytes(raw, encoding='utf-8'))
- root = tree #tree.getroot()
-
- last_comment = None
- for child in root:
- # TODO: handle plurals
- if isinstance(child, ET._Comment):
- last_comment = child
- elif child.tag == "string":
- # We always consume comment
- comment = last_comment
- last_comment = None
-
- # Prepare string for analysis
- text = "".join(child.itertext())
- sample = "".join(sample_iter(child)).strip().strip("'\"")
-
- # Validate comment
- if comment is None:
- info(child, "Missing string comment to aid translation",
- None, None)
- continue
- if "do not translate" in comment.text.lower():
- continue
- if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
- continue
-
- misspelled_attributes = [
- ("translateable", "translatable"),
- ]
- for misspelling, expected in misspelled_attributes:
- if misspelling in child.attrib:
- error(child, "Misspelled <string> attribute.", misspelling, expected)
-
- limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
- if limit is None:
- info(child, "Missing CHAR LIMIT to aid translation",
- repr(comment), "<!-- Description of string [CHAR LIMIT=32] -->")
- elif re.match("\d+", limit.group(1)):
- limit = int(limit.group(1))
- if len(sample) > limit:
- warn(child, "Expanded string length is larger than CHAR LIMIT",
- sample, None)
-
- # Look for common mistakes/substitutions
- if "'" in text:
- error(child, "Turned quotation mark glyphs are more polished",
- text, "This doesn\u2019t need to \u2018happen\u2019 today")
- if '"' in text and not text.startswith('"') and text.endswith('"'):
- error(child, "Turned quotation mark glyphs are more polished",
- text, "This needs to \u201chappen\u201d today")
- if "..." in text:
- error(child, "Ellipsis glyph is more polished",
- text, "Loading\u2026")
- if "wi-fi" in text.lower():
- error(child, "Non-breaking glyph is more polished",
- text, "Wi\u2011Fi")
- if "wifi" in text.lower():
- error(child, "Using non-standard spelling",
- text, "Wi\u2011Fi")
- if re.search("\d-\d", text):
- warn(child, "Ranges should use en dash glyph",
- text, "You will find this material in chapters 8\u201312")
- if "--" in text:
- warn(child, "Phrases should use em dash glyph",
- text, "Upon discovering errors\u2014all 124 of them\u2014they recalled.")
- if ". " in text:
- warn(child, "Only use single space between sentences",
- text, "First idea. Second idea.")
- if re.match(r"^[A-Z\s]{5,}$", text):
- warn(child, "Actions should use android:textAllCaps in layout; ignore if acronym",
- text, "Refresh data")
- if " phone " in text and "product" not in child.attrib:
- warn(child, "Strings mentioning phones should have variants for tablets",
- text, None)
-
- # When more than one substitution, require indexes
- if len(re.findall("%[^%]", text)) > 1:
- if len(re.findall("%[^\d]", text)) > 0:
- error(child, "Substitutions must be indexed",
- text, "Add %1$s to %2$s")
-
- # Require xliff substitutions
- for gc in child.iter():
- badsub = False
- if gc.tail and re.search("%[^%]", gc.tail): badsub = True
- if re.match("{.*xliff.*}g", gc.tag):
- if "id" not in gc.attrib:
- error(child, "Substitutions must define id attribute",
- None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
- if "example" not in gc.attrib:
- error(child, "Substitutions must define example attribute",
- None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
- else:
- if gc.text and re.search("%[^%]", gc.text): badsub = True
- if badsub:
- error(child, "Substitutions must be inside xliff tags",
- text, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
-
- return warnings
-
-if len(sys.argv) > 2:
- before = lint(sys.argv[2])
-else:
- before = {}
-after = lint(sys.argv[1])
-
-for b in before:
- if b in after:
- del after[b]
-
-if len(after) > 0:
- for a in sorted(after.keys()):
- print(after[a])
- print()
- sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd05698..009a1f2 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,3 @@
#!/bin/bash
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
- python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
-done
+
+# NOTE: this script has been deprecated and replaced by AyeAye checks directly in Gerrit
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
index b2c5f4a..8e62388 100644
--- a/tools/xmlpersistence/src/main/kotlin/Generator.kt
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -149,6 +149,7 @@
when (field) {
is ClassFieldInfo -> this += field.allClassFields
is ListFieldInfo -> this += field.element.allClassFields
+ else -> {}
}
}
}