diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 7393bcde13b62940c9e6f165946c913bf1222286..21ed1eb6bef8e8a309f8e5affbef3117f7c6d957 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -37,7 +37,9 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.os.WorkSource; import android.text.TextUtils; import android.util.Log; @@ -91,6 +93,14 @@ import java.util.concurrent.Executor; public class AlarmManager { private static final String TAG = "AlarmManager"; + /** + * Prefix used by {{@link #makeTag(long, WorkSource)}} to make a tag on behalf of the caller + * when the {@link #set(int, long, long, long, OnAlarmListener, Handler, WorkSource)} API is + * used. This prefix is a unique sequence of characters to differentiate with other tags that + * apps may provide to other APIs that accept a listener callback. + */ + private static final String GENERATED_TAG_PREFIX = "$android.alarm.generated"; + /** @hide */ @IntDef(prefix = { "RTC", "ELAPSED" }, value = { RTC_WAKEUP, @@ -860,6 +870,24 @@ public class AlarmManager { targetHandler, workSource, null); } + /** + * This is only used to make an identifying tag for the deprecated + * {@link #set(int, long, long, long, OnAlarmListener, Handler, WorkSource)} API which doesn't + * accept a tag. For all other APIs, the tag provided by the app is used, even if it is + * {@code null}. + */ + private static String makeTag(long triggerMillis, WorkSource ws) { + final StringBuilder tagBuilder = new StringBuilder(GENERATED_TAG_PREFIX); + + tagBuilder.append(":"); + final int attributionUid = + (ws == null || ws.isEmpty()) ? Process.myUid() : ws.getAttributionUid(); + tagBuilder.append(UserHandle.formatUid(attributionUid)); + tagBuilder.append(":"); + tagBuilder.append(triggerMillis); + return tagBuilder.toString(); + } + /** * Direct callback version of {@link #set(int, long, long, long, PendingIntent, WorkSource)}. * Note that repeating alarms must use the PendingIntent variant, not an OnAlarmListener. @@ -875,8 +903,8 @@ public class AlarmManager { public void set(@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, OnAlarmListener listener, Handler targetHandler, WorkSource workSource) { - setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, null, - targetHandler, workSource, null); + setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, null, listener, + makeTag(triggerAtMillis, workSource), targetHandler, workSource, null); } /** diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 9b64edf53d8cf3660541f1cd75d11d0aaaca8381..f50a9024803000b6b544c81eb591eaea86db704d 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -225,6 +225,8 @@ public interface AppStandbyInternal { void setActiveAdminApps(Set adminPkgs, int userId); + void setAdminProtectedPackages(Set packageNames, int userId); + /** * @return {@code true} if the given package is an active device admin app. */ diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index af6334cbbd8688aadf89f9eb3e19fe5d128cffcd..342465138ac73f87536f723a78600b02120ddb9c 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -4739,8 +4739,14 @@ public class AlarmManagerService extends SystemService { } final ArraySet> triggerPackages = new ArraySet<>(); + final SparseIntArray countsPerUid = new SparseIntArray(); + final SparseIntArray wakeupCountsPerUid = new SparseIntArray(); for (int i = 0; i < triggerList.size(); i++) { final Alarm a = triggerList.get(i); + increment(countsPerUid, a.uid); + if (a.wakeup) { + increment(wakeupCountsPerUid, a.uid); + } if (mConstants.USE_TARE_POLICY) { if (!isExemptFromTare(a)) { triggerPackages.add(Pair.create( @@ -4761,7 +4767,8 @@ public class AlarmManagerService extends SystemService { } rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); - MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps); + logAlarmBatchDelivered( + triggerList.size(), wakeUps, countsPerUid, wakeupCountsPerUid); } } @@ -4776,6 +4783,32 @@ public class AlarmManagerService extends SystemService { } } + private static void increment(SparseIntArray array, int key) { + final int index = array.indexOfKey(key); + if (index >= 0) { + array.setValueAt(index, array.valueAt(index) + 1); + } else { + array.put(key, 1); + } + } + + private void logAlarmBatchDelivered( + int alarms, + int wakeups, + SparseIntArray countsPerUid, + SparseIntArray wakeupCountsPerUid) { + final int[] uids = new int[countsPerUid.size()]; + final int[] countsArray = new int[countsPerUid.size()]; + final int[] wakeupCountsArray = new int[countsPerUid.size()]; + for (int i = 0; i < countsPerUid.size(); i++) { + uids[i] = countsPerUid.keyAt(i); + countsArray[i] = countsPerUid.valueAt(i); + wakeupCountsArray[i] = wakeupCountsPerUid.get(uids[i], 0); + } + MetricsHelper.pushAlarmBatchDelivered( + alarms, wakeups, uids, countsArray, wakeupCountsArray); + } + /** * Attribute blame for a WakeLock. * @@ -5695,12 +5728,7 @@ public class AlarmManagerService extends SystemService { } private void incrementAlarmCount(int uid) { - final int uidIndex = mAlarmsPerUid.indexOfKey(uid); - if (uidIndex >= 0) { - mAlarmsPerUid.setValueAt(uidIndex, mAlarmsPerUid.valueAt(uidIndex) + 1); - } else { - mAlarmsPerUid.put(uid, 1); - } + increment(mAlarmsPerUid, uid); } /** diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java index 75ed616e2d96467009f6da9aacb051d78cc36c7f..2923cfd8e22cc2b6055f0193fd27011f9a4ab641 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/MetricsHelper.java @@ -111,10 +111,14 @@ class MetricsHelper { ActivityManager.processStateAmToProto(callerProcState)); } - static void pushAlarmBatchDelivered(int numAlarms, int wakeups) { + static void pushAlarmBatchDelivered( + int numAlarms, int wakeups, int[] uids, int[] alarmsPerUid, int[] wakeupAlarmsPerUid) { FrameworkStatsLog.write( FrameworkStatsLog.ALARM_BATCH_DELIVERED, numAlarms, - wakeups); + wakeups, + uids, + alarmsPerUid, + wakeupAlarmsPerUid); } } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index a6f47d4e4908180fb7164bb795b13af605333064..b27ff411dd584b6abd68f2a6d65f29cc8cd74157 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -264,6 +264,10 @@ public class AppStandbyController @GuardedBy("mActiveAdminApps") private final SparseArray> mActiveAdminApps = new SparseArray<>(); + /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */ + @GuardedBy("mAdminProtectedPackages") + private final SparseArray> mAdminProtectedPackages = new SparseArray<>(); + /** * Set of system apps that are headless (don't have any "front door" activities, enabled or * disabled). Presence in this map indicates that the app is a headless system app. @@ -1335,6 +1339,9 @@ public class AppStandbyController synchronized (mActiveAdminApps) { mActiveAdminApps.remove(userId); } + synchronized (mAdminProtectedPackages) { + mAdminProtectedPackages.remove(userId); + } } } @@ -1424,6 +1431,10 @@ public class AppStandbyController return STANDBY_BUCKET_EXEMPTED; } + if (isAdminProtectedPackages(packageName, userId)) { + return STANDBY_BUCKET_EXEMPTED; + } + if (isActiveNetworkScorer(packageName)) { return STANDBY_BUCKET_EXEMPTED; } @@ -1871,6 +1882,17 @@ public class AppStandbyController } } + private boolean isAdminProtectedPackages(String packageName, int userId) { + synchronized (mAdminProtectedPackages) { + if (mAdminProtectedPackages.contains(UserHandle.USER_ALL) + && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) { + return true; + } + return mAdminProtectedPackages.contains(userId) + && mAdminProtectedPackages.get(userId).contains(packageName); + } + } + @Override public void addActiveDeviceAdmin(String adminPkg, int userId) { synchronized (mActiveAdminApps) { @@ -1894,6 +1916,17 @@ public class AppStandbyController } } + @Override + public void setAdminProtectedPackages(Set packageNames, int userId) { + synchronized (mAdminProtectedPackages) { + if (packageNames == null || packageNames.isEmpty()) { + mAdminProtectedPackages.remove(userId); + } else { + mAdminProtectedPackages.put(userId, packageNames); + } + } + } + @Override public void onAdminDataAvailable() { mAdminDataAvailableLatch.countDown(); @@ -1916,6 +1949,13 @@ public class AppStandbyController } } + @VisibleForTesting + Set getAdminProtectedPackagesForTest(int userId) { + synchronized (mAdminProtectedPackages) { + return mAdminProtectedPackages.get(userId); + } + } + /** * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, * returns {@code false}. diff --git a/config/preloaded-classes b/config/preloaded-classes index a6c0c3d7eb15fbcc0d165fb79dce933e754aac44..6de1461cc0df9c185f1a7b1edc5d6ab5367eac33 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -14414,6 +14414,7 @@ java.util.ImmutableCollections$Set0 java.util.ImmutableCollections$Set1 java.util.ImmutableCollections$Set2 java.util.ImmutableCollections$SetN +java.util.ImmutableCollections java.util.InputMismatchException java.util.Iterator java.util.JumboEnumSet$EnumSetIterator diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6e61569a835255daaf4940197124a395979ccddb..04b48ceadf967743fe703136e7c05486cd01a9d7 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -809,6 +809,7 @@ package android.content.pm { field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f; field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L + field public static final long OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS = 237531167L; // 0xe28701fL field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2 } @@ -1151,7 +1152,6 @@ package android.hardware.camera2 { public final class CameraManager { method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException; method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; - field public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; // 0xef10e60L } public abstract static class CameraManager.AvailabilityCallback { @@ -2442,6 +2442,7 @@ package android.service.dreams { public abstract class DreamOverlayService extends android.app.Service { ctor public DreamOverlayService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public void onEndDream(); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); method public final void requestExit(); method public final boolean shouldShowComplications(); @@ -2947,7 +2948,9 @@ package android.view { } @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { + method public void getBoundsOnScreen(@NonNull android.graphics.Rect, boolean); method public android.view.View getTooltipView(); + method public void getWindowDisplayFrame(@NonNull android.graphics.Rect); method public boolean isAutofilled(); method public static boolean isDefaultFocusHighlightEnabled(); method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 1807fb66da8f600d80a57e0317eaf63fa3ef77d3..cf11c5c7d431f6a90be164109240411f1b3a9bff 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -997,6 +997,8 @@ public class Activity extends ContextThemeWrapper private ComponentCallbacksController mCallbacksController; + @Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService; + private final WindowControllerCallback mWindowControllerCallback = new WindowControllerCallback() { /** @@ -1606,18 +1608,17 @@ public class Activity extends ContextThemeWrapper private void notifyVoiceInteractionManagerServiceActivityEvent( @VoiceInteractionSession.VoiceInteractionActivityEventType int type) { - - final IVoiceInteractionManagerService service = - IVoiceInteractionManagerService.Stub.asInterface( - ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); - if (service == null) { - Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get " - + "VoiceInteractionManagerService"); - return; + if (mVoiceInteractionManagerService == null) { + mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface( + ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + if (mVoiceInteractionManagerService == null) { + Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get " + + "VoiceInteractionManagerService"); + return; + } } - try { - service.notifyActivityEventChanged(mToken, type); + mVoiceInteractionManagerService.notifyActivityEventChanged(mToken, type); } catch (RemoteException e) { // Empty } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9328664683143ded89e903bdee15ff33b2a6616c..fec522b7ff7cff948ec187ec81826dcc7f84f3a7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4061,6 +4061,9 @@ public class ActivityManager { * processes to reclaim memory; the system will take care of restarting * these processes in the future as needed. * + *

Third party applications can only use this API to kill their own processes. + *

+ * * @param packageName The name of the package whose processes are to * be killed. */ diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index e00329385ca554b56c20f0729eb1f7746d2e0a32..53e0a05b27b1ca7ecf33703a0d1183b39acb173b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -326,6 +326,20 @@ public class ActivityOptions extends ComponentOptions { private static final String KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES = "android:activity.applyActivityFlagsForBubbles"; + /** + * Indicates to apply {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} to the launching shortcut. + * @hide + */ + private static final String KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT = + "android:activity.applyMultipleTaskFlagForShortcut"; + + /** + * Indicates to apply {@link Intent#FLAG_ACTIVITY_NO_USER_ACTION} to the launching shortcut. + * @hide + */ + private static final String KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT = + "android:activity.applyNoUserActionFlagForShortcut"; + /** * For Activity transitions, the calling Activity's TransitionListener used to * notify the called Activity when the shared element and the exit transitions @@ -459,6 +473,8 @@ public class ActivityOptions extends ComponentOptions { private boolean mLockTaskMode = false; private boolean mDisallowEnterPictureInPictureWhileLaunching; private boolean mApplyActivityFlagsForBubbles; + private boolean mApplyMultipleTaskFlagForShortcut; + private boolean mApplyNoUserActionFlagForShortcut; private boolean mTaskAlwaysOnTop; private boolean mTaskOverlay; private boolean mTaskOverlayCanResume; @@ -1258,6 +1274,10 @@ public class ActivityOptions extends ComponentOptions { KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false); mApplyActivityFlagsForBubbles = opts.getBoolean( KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, false); + mApplyMultipleTaskFlagForShortcut = opts.getBoolean( + KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT, false); + mApplyNoUserActionFlagForShortcut = opts.getBoolean( + KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT, false); if (opts.containsKey(KEY_ANIM_SPECS)) { Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); mAnimSpecs = new AppTransitionAnimationSpec[specs.length]; @@ -1844,6 +1864,26 @@ public class ActivityOptions extends ComponentOptions { return mApplyActivityFlagsForBubbles; } + /** @hide */ + public void setApplyMultipleTaskFlagForShortcut(boolean apply) { + mApplyMultipleTaskFlagForShortcut = apply; + } + + /** @hide */ + public boolean isApplyMultipleTaskFlagForShortcut() { + return mApplyMultipleTaskFlagForShortcut; + } + + /** @hide */ + public void setApplyNoUserActionFlagForShortcut(boolean apply) { + mApplyNoUserActionFlagForShortcut = apply; + } + + /** @hide */ + public boolean isApplyNoUserActionFlagForShortcut() { + return mApplyNoUserActionFlagForShortcut; + } + /** * Sets a launch cookie that can be used to track the activity and task that are launch as a * result of this option. If the launched activity is a trampoline that starts another activity @@ -2175,6 +2215,13 @@ public class ActivityOptions extends ComponentOptions { if (mApplyActivityFlagsForBubbles) { b.putBoolean(KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES, mApplyActivityFlagsForBubbles); } + if (mApplyMultipleTaskFlagForShortcut) { + b.putBoolean(KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT, + mApplyMultipleTaskFlagForShortcut); + } + if (mApplyNoUserActionFlagForShortcut) { + b.putBoolean(KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT, true); + } if (mAnimSpecs != null) { b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a1e4dbf0b50d62e4673be0759a9675ea984970ba..2c8264423f2dd0fba91d590ea91de7d8d406c38c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -29,6 +29,8 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS; import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; +import static android.content.res.Configuration.UI_MODE_TYPE_DESK; +import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; @@ -198,6 +200,7 @@ import android.window.SplashScreen; import android.window.SplashScreenView; import android.window.WindowProviderService; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; @@ -5906,9 +5909,21 @@ public final class ActivityThread extends ClientTransactionHandler final boolean shouldUpdateResources = hasPublicResConfigChange || shouldUpdateResources(activityToken, currentResConfig, newConfig, amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange); + + // TODO(b/266924897): temporary workaround, remove for U. + boolean skipActivityRelaunchWhenDocking = activity.getResources().getBoolean( + R.bool.config_skipActivityRelaunchWhenDocking); + int handledConfigChanges = activity.mActivityInfo.getRealConfigChanged(); + if (skipActivityRelaunchWhenDocking && onlyDeskInUiModeChanged(activity.mCurrentConfig, + newConfig)) { + // If we're not relaunching this activity when docking, we should send the configuration + // changed event. Pretend as if the activity is handling uiMode config changes in its + // manifest so that we'll report any dock changes. + handledConfigChanges |= ActivityInfo.CONFIG_UI_MODE; + } + final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig, - r != null ? r.mSizeConfigurations : null, - activity.mActivityInfo.getRealConfigChanged()); + r != null ? r.mSizeConfigurations : null, handledConfigChanges); // Nothing significant, don't proceed with updating and reporting. if (!shouldUpdateResources && !shouldReportChange) { return null; @@ -5954,6 +5969,25 @@ public final class ActivityThread extends ClientTransactionHandler return configToReport; } + /** + * Returns true if the uiMode configuration changed, and desk mode + * ({@link android.content.res.Configuration#UI_MODE_TYPE_DESK}) was the only change to uiMode. + */ + private boolean onlyDeskInUiModeChanged(Configuration oldConfig, Configuration newConfig) { + boolean deskModeChanged = isInDeskUiMode(oldConfig) != isInDeskUiMode(newConfig); + + // UI mode contains fields other than the UI mode type, so determine if any other fields + // changed. + boolean uiModeOtherFieldsChanged = + (oldConfig.uiMode & ~UI_MODE_TYPE_MASK) != (newConfig.uiMode & ~UI_MODE_TYPE_MASK); + + return deskModeChanged && !uiModeOtherFieldsChanged; + } + + private static boolean isInDeskUiMode(Configuration config) { + return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_DESK; + } + /** * Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be * dispatched. diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 28c273ec50a6eb1f2c2cc957d160877907b9b947..f6056bd39005d15ffe1cccd42a099f0ba2ecbaa7 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -205,6 +205,20 @@ interface IWallpaperManager { */ void notifyGoingToSleep(int x, int y, in Bundle extras); + /** + * Called when the screen has been fully turned on and is visible. + * + * @hide + */ + void notifyScreenTurnedOn(int displayId); + + /** + * Called when the screen starts turning on. + * + * @hide + */ + void notifyScreenTurningOn(int displayId); + /** * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 7f4af8b3dc78f686563a80546251071b0408aef4..90e3e9d1a9c33a2687143ce7e090b37a2c2e38f7 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6189,10 +6189,8 @@ public class Notification implements Parcelable private RemoteViews generateActionButton(Action action, boolean emphasizedMode, StandardTemplateParams p) { final boolean tombstone = (action.actionIntent == null); - RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), - emphasizedMode ? getEmphasizedActionLayoutResource() - : tombstone ? getActionTombstoneLayoutResource() - : getActionLayoutResource()); + final RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(), + getActionButtonLayoutResource(emphasizedMode, tombstone)); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); } @@ -6204,6 +6202,12 @@ public class Notification implements Parcelable // change the background bgColor CharSequence title = action.title; int buttonFillColor = getColors(p).getSecondaryAccentColor(); + if (tombstone) { + buttonFillColor = setAlphaComponentByFloatDimen(mContext, + ContrastColorUtil.resolveSecondaryColor( + mContext, getColors(p).getBackgroundColor(), mInNightMode), + R.dimen.notification_action_disabled_container_alpha); + } if (isLegacy()) { title = ContrastColorUtil.clearColorSpans(title); } else { @@ -6219,8 +6223,14 @@ public class Notification implements Parcelable title = ensureColorSpanContrast(title, buttonFillColor); } button.setTextViewText(R.id.action0, processTextSpans(title)); - final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext, + int textColor = ContrastColorUtil.resolvePrimaryColor(mContext, buttonFillColor, mInNightMode); + if (tombstone) { + textColor = setAlphaComponentByFloatDimen(mContext, + ContrastColorUtil.resolveSecondaryColor( + mContext, getColors(p).getBackgroundColor(), mInNightMode), + R.dimen.notification_action_disabled_content_alpha); + } button.setTextColor(R.id.action0, textColor); // We only want about 20% alpha for the ripple final int rippleColor = (textColor & 0x00ffffff) | 0x33000000; @@ -6250,6 +6260,26 @@ public class Notification implements Parcelable return button; } + private int getActionButtonLayoutResource(boolean emphasizedMode, boolean tombstone) { + if (emphasizedMode) { + return tombstone ? getEmphasizedTombstoneActionLayoutResource() + : getEmphasizedActionLayoutResource(); + } else { + return tombstone ? getActionTombstoneLayoutResource() + : getActionLayoutResource(); + } + } + + /** + * Set the alpha component of {@code color} to be {@code alphaDimenResId}. + */ + private static int setAlphaComponentByFloatDimen(Context context, @ColorInt int color, + @DimenRes int alphaDimenResId) { + final TypedValue alphaValue = new TypedValue(); + context.getResources().getValue(alphaDimenResId, alphaValue, true); + return ColorUtils.setAlphaComponent(color, Math.round(alphaValue.getFloat() * 255)); + } + /** * Extract the color from a full-length span from the text. * @@ -6729,6 +6759,10 @@ public class Notification implements Parcelable return R.layout.notification_material_action_emphasized; } + private int getEmphasizedTombstoneActionLayoutResource() { + return R.layout.notification_material_action_emphasized_tombstone; + } + private int getActionTombstoneLayoutResource() { return R.layout.notification_material_action_tombstone; } @@ -6935,8 +6969,10 @@ public class Notification implements Parcelable /** * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS * permission. The permission is checked when a notification is enqueued. + * + * @hide */ - private boolean hasColorizedPermission() { + public boolean hasColorizedPermission() { return (flags & Notification.FLAG_CAN_COLORIZE) != 0; } @@ -7948,8 +7984,6 @@ public class Notification implements Parcelable * @hide */ public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) { - // TODO(b/228941516): This icon should be downscaled to avoid using too much memory, - // see reduceImageSizes. mShortcutIcon = conversationIcon; return this; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index f6d27ad08b00980495c43eae8982ba8f934bff8d..37a90de8d6001f333bc2d4224a5c5c5bfd332a7d 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -317,7 +317,10 @@ public class NotificationManager { /** * Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes. - * This broadcast is only sent to registered receivers. + * + *

This broadcast is only sent to registered receivers and (starting from + * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not + * Disturb access (see {@link #isNotificationPolicyAccessGranted()}). * * @hide */ @@ -337,7 +340,10 @@ public class NotificationManager { /** * Intent that is broadcast when the state of getNotificationPolicy() changes. - * This broadcast is only sent to registered receivers. + * + *

This broadcast is only sent to registered receivers and (starting from + * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not + * Disturb access (see {@link #isNotificationPolicyAccessGranted()}). */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_NOTIFICATION_POLICY_CHANGED @@ -345,7 +351,10 @@ public class NotificationManager { /** * Intent that is broadcast when the state of getCurrentInterruptionFilter() changes. - * This broadcast is only sent to registered receivers. + * + *

This broadcast is only sent to registered receivers and (starting from + * {@link Build.VERSION_CODES#Q}) receivers in packages that have been granted Do Not + * Disturb access (see {@link #isNotificationPolicyAccessGranted()}). */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_INTERRUPTION_FILTER_CHANGED diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index a51b9d3956df247a9f50a2e687b748ee305dccf0..27f9f54d95229aef5958fb9f9b56b6752694cac3 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -1405,6 +1405,17 @@ public class PropertyInvalidatedCache { return isDisabled(); } + /** + * Return the number of entries in the cache. This is used for testing and has package-only + * visibility. + * @hide + */ + public int size() { + synchronized (mLock) { + return mCache.size(); + } + } + /** * Returns a list of caches alive at the current time. */ @@ -1612,8 +1623,12 @@ public class PropertyInvalidatedCache { * @hide */ public static void onTrimMemory() { - for (PropertyInvalidatedCache pic : getActiveCaches()) { - pic.clear(); + ArrayList activeCaches; + synchronized (sGlobalLock) { + activeCaches = getActiveCaches(); + } + for (int i = 0; i < activeCaches.size(); i++) { + activeCaches.get(i).clear(); } } } diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 3bf3067f8410d2fdeb90557022f9d1408115087a..1551ce9a91912bcd60460fcb16a46928d7319dd0 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -48,6 +48,12 @@ import java.util.Objects; public class TaskInfo { private static final String TAG = "TaskInfo"; + /** + * The value to use when the property has not a specific value. + * @hide + */ + public static final int PROPERTY_VALUE_UNSET = -1; + /** * The id of the user the task was running as if this is a leaf task. The id of the current * running user of the system otherwise. @@ -187,6 +193,14 @@ public class TaskInfo { */ public int launchIntoPipHostTaskId; + /** + * The task id of the parent Task of the launch-into-pip Activity, i.e., if task have more than + * one activity it will create new task for this activity, this id is the origin task id and + * the pip activity will be reparent to origin task when it exit pip mode. + * @hide + */ + public int lastParentTaskIdBeforePip; + /** * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS), @@ -221,6 +235,46 @@ public class TaskInfo { */ public boolean topActivityEligibleForLetterboxEducation; + /** + * Whether the double tap is enabled + * @hide + */ + public boolean isLetterboxDoubleTapEnabled; + + /** + * Whether the update comes from a letterbox double-tap action from the user or not. + * @hide + */ + public boolean isFromLetterboxDoubleTap; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or + * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise. + * @hide + */ + public int topActivityLetterboxVerticalPosition; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current letterbox vertical position or + * {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise. + * @hide + */ + public int topActivityLetterboxHorizontalPosition; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current width of the letterboxed + * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise + * @hide + */ + public int topActivityLetterboxWidth; + + /** + * If {@link isLetterboxDoubleTapEnabled} it contains the current height of the letterboxed + * activity or {@link TaskInfo.PROPERTY_VALUE_UNSET} otherwise + * @hide + */ + public int topActivityLetterboxHeight; + /** * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity * supports), this is what the system actually uses for resizability based on other policy and @@ -399,7 +453,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { return hasCameraCompatControl() || topActivityInSizeCompat - || topActivityEligibleForLetterboxEducation; + || topActivityEligibleForLetterboxEducation + || isLetterboxDoubleTapEnabled; } /** @@ -439,11 +494,18 @@ public class TaskInfo { && isResizeable == that.isResizeable && supportsMultiWindow == that.supportsMultiWindow && displayAreaFeatureId == that.displayAreaFeatureId + && isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap + && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition + && topActivityLetterboxWidth == that.topActivityLetterboxWidth + && topActivityLetterboxHeight == that.topActivityLetterboxHeight + && topActivityLetterboxHorizontalPosition + == that.topActivityLetterboxHorizontalPosition && Objects.equals(positionInParent, that.positionInParent) && Objects.equals(pictureInPictureParams, that.pictureInPictureParams) && Objects.equals(shouldDockBigOverlays, that.shouldDockBigOverlays) && Objects.equals(displayCutoutInsets, that.displayCutoutInsets) && getWindowingMode() == that.getWindowingMode() + && configuration.uiMode == that.configuration.uiMode && Objects.equals(taskDescription, that.taskDescription) && isFocused == that.isFocused && isVisible == that.isVisible @@ -464,14 +526,21 @@ public class TaskInfo { return displayId == that.displayId && taskId == that.taskId && topActivityInSizeCompat == that.topActivityInSizeCompat + && isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap && topActivityEligibleForLetterboxEducation == that.topActivityEligibleForLetterboxEducation + && topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition + && topActivityLetterboxHorizontalPosition + == that.topActivityLetterboxHorizontalPosition + && topActivityLetterboxWidth == that.topActivityLetterboxWidth + && topActivityLetterboxHeight == that.topActivityLetterboxHeight && cameraCompatControlState == that.cameraCompatControlState // Bounds are important if top activity has compat controls. && (!hasCompatUI() || configuration.windowConfiguration.getBounds() .equals(that.configuration.windowConfiguration.getBounds())) && (!hasCompatUI() || configuration.getLayoutDirection() == that.configuration.getLayoutDirection()) + && (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode) && (!hasCompatUI() || isVisible == that.isVisible); } @@ -501,6 +570,7 @@ public class TaskInfo { pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR); shouldDockBigOverlays = source.readBoolean(); launchIntoPipHostTaskId = source.readInt(); + lastParentTaskIdBeforePip = source.readInt(); displayCutoutInsets = source.readTypedObject(Rect.CREATOR); topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR); isResizeable = source.readBoolean(); @@ -518,6 +588,12 @@ public class TaskInfo { mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); displayAreaFeatureId = source.readInt(); cameraCompatControlState = source.readInt(); + isLetterboxDoubleTapEnabled = source.readBoolean(); + isFromLetterboxDoubleTap = source.readBoolean(); + topActivityLetterboxVerticalPosition = source.readInt(); + topActivityLetterboxHorizontalPosition = source.readInt(); + topActivityLetterboxWidth = source.readInt(); + topActivityLetterboxHeight = source.readInt(); } /** @@ -547,6 +623,7 @@ public class TaskInfo { dest.writeTypedObject(pictureInPictureParams, flags); dest.writeBoolean(shouldDockBigOverlays); dest.writeInt(launchIntoPipHostTaskId); + dest.writeInt(lastParentTaskIdBeforePip); dest.writeTypedObject(displayCutoutInsets, flags); dest.writeTypedObject(topActivityInfo, flags); dest.writeBoolean(isResizeable); @@ -564,6 +641,12 @@ public class TaskInfo { dest.writeTypedObject(mTopActivityLocusId, flags); dest.writeInt(displayAreaFeatureId); dest.writeInt(cameraCompatControlState); + dest.writeBoolean(isLetterboxDoubleTapEnabled); + dest.writeBoolean(isFromLetterboxDoubleTap); + dest.writeInt(topActivityLetterboxVerticalPosition); + dest.writeInt(topActivityLetterboxHorizontalPosition); + dest.writeInt(topActivityLetterboxWidth); + dest.writeInt(topActivityLetterboxHeight); } @Override @@ -587,6 +670,7 @@ public class TaskInfo { + " pictureInPictureParams=" + pictureInPictureParams + " shouldDockBigOverlays=" + shouldDockBigOverlays + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId + + " lastParentTaskIdBeforePip=" + lastParentTaskIdBeforePip + " displayCutoutSafeInsets=" + displayCutoutInsets + " topActivityInfo=" + topActivityInfo + " launchCookies=" + launchCookies @@ -598,6 +682,13 @@ public class TaskInfo { + " topActivityInSizeCompat=" + topActivityInSizeCompat + " topActivityEligibleForLetterboxEducation= " + topActivityEligibleForLetterboxEducation + + " topActivityLetterboxed= " + isLetterboxDoubleTapEnabled + + " isFromDoubleTap= " + isFromLetterboxDoubleTap + + " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition + + " topActivityLetterboxHorizontalPosition= " + + topActivityLetterboxHorizontalPosition + + " topActivityLetterboxWidth=" + topActivityLetterboxWidth + + " topActivityLetterboxHeight=" + topActivityLetterboxHeight + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 067a4c3c047ea348adea44def2f1a15946ff90f0..a34a50c4b7b07bc49ad6ecf5bcd28eb84af06cb9 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemProperties; import android.util.Log; import android.util.MathUtils; import android.util.Size; @@ -101,11 +102,13 @@ public final class WallpaperColors implements Parcelable { // Decides when dark theme is optimal for this wallpaper private static final float DARK_THEME_MEAN_LUMINANCE = 0.3f; // Minimum mean luminosity that an image needs to have to support dark text - private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.7f; + private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = SystemProperties.getInt( + "persist.wallpapercolors.threshold", 70) / 100f; // We also check if the image has dark pixels in it, // to avoid bright images with some dark spots. private static final float DARK_PIXEL_CONTRAST = 5.5f; - private static final float MAX_DARK_AREA = 0.05f; + private static final float MAX_DARK_AREA = SystemProperties.getInt( + "persist.wallpapercolors.max_dark_area", 5) / 100f; private final List mMainColors; private final Map mAllColors; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 67d51481830f0792a4011b41c83cc39ba1a834df..d99e4575ea4246a2485b0e866eab53e57d75adbb 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6639,6 +6639,13 @@ public class DevicePolicyManager { */ public static final int KEYGUARD_DISABLE_IRIS = 1 << 8; + /** + * Disable all keyguard shortcuts. + * + * @hide + */ + public static final int KEYGUARD_DISABLE_SHORTCUTS_ALL = 1 << 9; + /** * NOTE: Please remember to update the DevicePolicyManagerTest's testKeyguardDisabledFeatures * CTS test when adding to the list above. @@ -6682,7 +6689,8 @@ public class DevicePolicyManager { */ public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY = DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA - | DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; + | DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS + | DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL; /** * Keyguard features that when set on a normal or organization-owned managed profile, have @@ -6789,6 +6797,8 @@ public class DevicePolicyManager { * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, * {@link #ENCRYPTION_STATUS_ACTIVATING}, {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, * {@link #ENCRYPTION_STATUS_ACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}. + * + * @throws SecurityException if called on a parent instance. */ public int getStorageEncryptionStatus() { throwIfParentInstance("getStorageEncryptionStatus"); @@ -9180,7 +9190,8 @@ public class DevicePolicyManager { * @see #isProfileOwnerApp * @see #isDeviceOwnerApp * @param admin Which {@link DeviceAdminReceiver} this request is associate with. - * @param profileName The name of the profile. + * @param profileName The name of the profile. If the name is longer than 200 characters + * it will be truncated. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public void setProfileName(@NonNull ComponentName admin, String profileName) { @@ -14627,7 +14638,8 @@ public class DevicePolicyManager { /** * Called by a device owner or a profile owner to disable user control over apps. User will not * be able to clear app data or force-stop packages. When called by a device owner, applies to - * all users on the device. + * all users on the device. Packages with user control disabled are exempted from + * App Standby Buckets. * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param packages The package names for the apps. diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index fe10b7f8b3f4e4818daa277da45303f1dcd7400d..27f6a266597cd4a502a918a3b4fd3b8b509fef9c 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; @@ -311,19 +312,26 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -953,4 +961,15 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW reapplyLastRemoteViews(); } } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 77072890a1eb0b40485f3a68825139e24dad1ab6..856bde870bcfef5f711269d3f5afa4aa7376b949 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -69,6 +69,12 @@ public final class ContentCaptureOptions implements Parcelable { */ public final int logHistorySize; + /** + * Disable flush when receiving a VIEW_TREE_APPEARING event. + * @hide + */ + public final boolean disableFlushForViewTreeAppearing; + /** * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). @@ -90,7 +96,8 @@ public final class ContentCaptureOptions implements Parcelable { public ContentCaptureOptions(int loggingLevel) { this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, - /* logHistorySize= */ 0, /* whitelistedComponents= */ null); + /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, + /* whitelistedComponents= */ null); } /** @@ -98,10 +105,23 @@ public final class ContentCaptureOptions implements Parcelable { */ public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, - @SuppressLint("NullableCollection") + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + @Nullable ArraySet whitelistedComponents) { + this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, logHistorySize, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); + } + + /** @hide */ + public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable ArraySet whitelistedComponents) { this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents); + textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, + whitelistedComponents); } /** @hide */ @@ -111,11 +131,14 @@ public final class ContentCaptureOptions implements Parcelable { ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, - ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, whitelistedComponents); + ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, + ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + whitelistedComponents); } private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, + boolean disableFlushForViewTreeAppearing, @Nullable ArraySet whitelistedComponents) { this.lite = lite; this.loggingLevel = loggingLevel; @@ -123,6 +146,7 @@ public final class ContentCaptureOptions implements Parcelable { this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; this.logHistorySize = logHistorySize; + this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; this.whitelistedComponents = whitelistedComponents; } @@ -171,7 +195,8 @@ public final class ContentCaptureOptions implements Parcelable { .append(", maxBufferSize=").append(maxBufferSize) .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) - .append(", logHistorySize=").append(logHistorySize); + .append(", logHistorySize=").append(logHistorySize) + .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { string.append(", whitelisted=").append(whitelistedComponents); } @@ -189,6 +214,7 @@ public final class ContentCaptureOptions implements Parcelable { pw.print(", idle="); pw.print(idleFlushingFrequencyMs); pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); pw.print(", logSize="); pw.print(logHistorySize); + pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); if (whitelistedComponents != null) { pw.print(", whitelisted="); pw.print(whitelistedComponents); } @@ -209,6 +235,7 @@ public final class ContentCaptureOptions implements Parcelable { parcel.writeInt(idleFlushingFrequencyMs); parcel.writeInt(textChangeFlushingFrequencyMs); parcel.writeInt(logHistorySize); + parcel.writeBoolean(disableFlushForViewTreeAppearing); parcel.writeArraySet(whitelistedComponents); } @@ -226,12 +253,13 @@ public final class ContentCaptureOptions implements Parcelable { final int idleFlushingFrequencyMs = parcel.readInt(); final int textChangeFlushingFrequencyMs = parcel.readInt(); final int logHistorySize = parcel.readInt(); + final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); @SuppressWarnings("unchecked") final ArraySet whitelistedComponents = (ArraySet) parcel.readArraySet(null); return new ContentCaptureOptions(loggingLevel, maxBufferSize, idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, - whitelistedComponents); + disableFlushForViewTreeAppearing, whitelistedComponents); } @Override diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index a320f1e9509c40fd7a575dd6660d8a3e48b5149a..809dc3c411887cdcf2bd4127c2a808af158292eb 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5737,18 +5737,17 @@ public class Intent implements Parcelable, Cloneable { * @hide */ public static final String EXTRA_CHOOSER_CUSTOM_ACTIONS = - "android.intent.extra.EXTRA_CHOOSER_CUSTOM_ACTIONS"; + "android.intent.extra.CHOOSER_CUSTOM_ACTIONS"; /** * Optional argument to be used with {@link #ACTION_CHOOSER}. - * A {@link android.app.PendingIntent} to be sent when the user wants to do payload reselection - * in the sharesheet. - * A reselection action allows the user to return to the source app to change the content being - * shared. + * A {@link android.app.PendingIntent} to be sent when the user wants to modify the content that + * they're sharing. This can be used to allow the user to return to the source app to, for + * example, select different media. * @hide */ - public static final String EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION = - "android.intent.extra.EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION"; + public static final String EXTRA_CHOOSER_MODIFY_SHARE_ACTION = + "android.intent.extra.CHOOSER_MODIFY_SHARE_ACTION"; /** * An {@code ArrayList} of {@code String} annotations describing content for diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 9e5e8deda84b26a27419d4a73abb8e2246d643d9..319f1298a87fb674e3d71924b5f3c741a25092f2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1034,6 +1034,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = 254631730L; // buganizer id + /** + * This change id enables compat policy that ignores app requested orientation in + * response to an app calling {@link android.app.Activity#setRequestedOrientation} more + * than twice in one second if an activity is not letterboxed for fixed orientation. + * See com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation + * for details. + * @hide + */ + @ChangeId + @Overridable + @Disabled + public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = + 273509367L; // buganizer id + /** * This change id forces the packages it is applied to never have Display API sandboxing * applied for a letterbox or SCM activity. The Display APIs will continue to provide @@ -1057,6 +1071,80 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { @TestApi public static final long ALWAYS_SANDBOX_DISPLAY_APIS = 185004937L; // buganizer id + /** + * This change id excludes the packages it is applied to from ignoreOrientationRequest behaviour + * that can be enabled by the device manufacturers for the com.android.server.wm.DisplayArea + * or for the whole display. + * @hide + */ + @ChangeId + @Overridable + @Disabled + public static final long OVERRIDE_RESPECT_REQUESTED_ORIENTATION = 236283604L; // buganizer id + + /** + * This change id excludes the packages it is applied to from the camera compat force rotation + * treatment. See com.android.server.wm.DisplayRotationCompatPolicy for context. + * @hide + */ + @ChangeId + @Overridable + @Disabled + public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION = + 263959004L; // buganizer id + + /** + * This change id excludes the packages it is applied to from activity refresh after camera + * compat force rotation treatment. See com.android.server.wm.DisplayRotationCompatPolicy for + * context. + * @hide + */ + @ChangeId + @Overridable + @Disabled + public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH = 264304459L; // buganizer id + + /** + * This change id makes the packages it is applied to do activity refresh after camera compat + * force rotation treatment using "resumed -> paused -> resumed" cycle rather than "resumed -> + * ... -> stopped -> ... -> resumed" cycle. See + * com.android.server.wm.DisplayRotationCompatPolicy for context. + * @hide + */ + @ChangeId + @Overridable + @Disabled + public static final long OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = + 264301586L; // buganizer id + + /** + * This change id forces the packages it is applied to sandbox {@link android.view.View} API to + * an activity bounds for: + * + *

{@link android.view.View#getLocationOnScreen}, + * {@link android.view.View#getWindowVisibleDisplayFrame}, + * {@link android.view.View}#getWindowDisplayFrame, + * {@link android.view.View}#getBoundsOnScreen. + * + *

For {@link android.view.View#getWindowVisibleDisplayFrame} and + * {@link android.view.View}#getWindowDisplayFrame this sandboxing is happening indirectly + * through + * {@link android.view.ViewRootImpl}#getWindowVisibleDisplayFrame, + * {@link android.view.ViewRootImpl}#getDisplayFrame respectively. + * + *

Some applications assume that they occupy the whole screen and therefore use the display + * coordinates in their calculations as if an activity is positioned in the top-left corner of + * the screen, with left coordinate equal to 0. This may not be the case of applications in + * multi-window and in letterbox modes. This can lead to shifted or out of bounds UI elements in + * case the activity is Letterboxed or is in multi-window mode. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS = 237531167L; // buganizer id + /** * This change id is the gatekeeper for all treatments that force a given min aspect ratio. * Enabling this change will allow the following min aspect ratio treatments to be applied: @@ -1152,6 +1240,91 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { @Overridable public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L; + // Compat framework that per-app overrides rely on only supports booleans. That's why we have + // multiple OVERRIDE_*_ORIENTATION_* change ids below instead of just one override with + // the integer value. + + /** + * Enables {@link #SCREEN_ORIENTATION_PORTRAIT}. Unless OVERRIDE_ANY_ORIENTATION + * is enabled, this override is used only when no other fixed orientation was specified by the + * activity. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L; + + /** + * Enables {@link #SCREEN_ORIENTATION_NOSENSOR}. Unless OVERRIDE_ANY_ORIENTATION + * is enabled, this override is used only when no other fixed orientation was specified by the + * activity. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR = 265451093L; + + /** + * Enables {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE}. Unless OVERRIDE_ANY_ORIENTATION + * is enabled, this override is used only when activity specify landscape orientation. + * This can help apps that assume that landscape display orientation corresponds to {@link + * android.view.Surface#ROTATION_90}, while on some devices it can be {@link + * android.view.Surface#ROTATION_270}. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L; + + /** + * When enabled, allows OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE, + * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT + * to override any orientation requested by the activity. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_ANY_ORIENTATION = 265464455L; + + /** + * When enabled, activates OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE, + * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT + * only when an app is connected to the camera. See + * com.android.server.wm.DisplayRotationCompatPolicy for more context. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA = 265456536L; + + /** + * This override fixes display orientation to landscape natural orientation when a task is + * fullscreen. While display rotation is fixed to landscape, the orientation requested by the + * activity will be still respected by bounds resolution logic. For instance, if an activity + * requests portrait orientation and this override is set, then activity will appear in the + * letterbox mode for fixed orientation with the display rotated to the lanscape natural + * orientation. + * + *

This override is applicable only when natural orientation of the device is + * landscape and display ignores orientation requestes. + * + *

Main use case for this override are camera-using activities that are portrait-only and + * assume alignment with natural device orientation. Such activities can automatically be + * rotated with com.android.server.wm.DisplayRotationCompatPolicy but not all of them can + * handle dynamic rotation and thus can benefit from this override. + * + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L; + /** * Compares activity window layout min width/height with require space for multi window to * determine if it can be put into multi window mode. @@ -1370,8 +1543,19 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @hide */ public boolean isFixedOrientation() { - return isFixedOrientationLandscape() || isFixedOrientationPortrait() - || screenOrientation == SCREEN_ORIENTATION_LOCKED; + return isFixedOrientation(screenOrientation); + } + + /** + * Returns true if the passed activity's orientation is fixed. + * @hide + */ + public static boolean isFixedOrientation(@ScreenOrientation int orientation) { + return orientation == SCREEN_ORIENTATION_LOCKED + // Orientation is fixed to natural display orientation + || orientation == SCREEN_ORIENTATION_NOSENSOR + || isFixedOrientationLandscape(orientation) + || isFixedOrientationPortrait(orientation); } /** diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index c8bbb0c1994d6cdf8c7fcf8d305029b693e4a4bb..6f09e79bdba9659668f7ebf68b8eb166faf45586 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1452,6 +1452,16 @@ public final class AssetManager implements AutoCloseable { } } + /** + * @hide + */ + Configuration[] getSizeAndUiModeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeAndUiModeConfigurations(mObject); + } + } + /** * Change the configuration used when retrieving resources. Not for use by * applications. @@ -1603,6 +1613,7 @@ public final class AssetManager implements AutoCloseable { private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + private static native @Nullable Configuration[] nativeGetSizeAndUiModeConfigurations(long ptr); private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled); private static native @Nullable String nativeGetLastResourceResolution(long ptr); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index a03286d3ec6f67842108b1bd851495feb109d2c3..9b169499b41fc889a68eb6e1be246f504f74a111 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -2207,6 +2207,11 @@ public class Resources { return mResourcesImpl.getSizeConfigurations(); } + /** @hide */ + public Configuration[] getSizeAndUiModeConfigurations() { + return mResourcesImpl.getSizeAndUiModeConfigurations(); + } + /** * Return the compatibility mode information for the application. * The returned object should be treated as read-only. diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index ff072916292be1046b5c724946f443e4c327d04c..3bb237ac12287c63f825595014fee309d1e17ab1 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -203,6 +203,10 @@ public class ResourcesImpl { return mAssets.getSizeConfigurations(); } + Configuration[] getSizeAndUiModeConfigurations() { + return mAssets.getSizeAndUiModeConfigurations(); + } + CompatibilityInfo getCompatibilityInfo() { return mDisplayAdjustments.getCompatibilityInfo(); } diff --git a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java index 41f2f9c28349690a735cb0a2c57d94793b5605d6..b86b97c76740dde20d31e0740b423359f96477c4 100644 --- a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java +++ b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java @@ -17,7 +17,7 @@ package android.database.sqlite; /** - * Thrown if the the bind or column parameter index is out of range + * Thrown if the bind or column parameter index is out of range. */ public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException { public SQLiteBindOrColumnIndexOutOfRangeException() {} diff --git a/core/java/android/database/sqlite/SQLiteDiskIOException.java b/core/java/android/database/sqlite/SQLiteDiskIOException.java index 01b2069c23db37c7884f845765e24a650e389714..152d90a76ba6b4cea785574ace6133defbea69e1 100644 --- a/core/java/android/database/sqlite/SQLiteDiskIOException.java +++ b/core/java/android/database/sqlite/SQLiteDiskIOException.java @@ -17,7 +17,7 @@ package android.database.sqlite; /** - * An exception that indicates that an IO error occured while accessing the + * Indicates that an IO error occurred while accessing the * SQLite database file. */ public class SQLiteDiskIOException extends SQLiteException { diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 5291d2b7389184ef9f6e57ac1defefb55bdc4400..ccc39b6080d7b4198f60b8f6ad9e9be5725654f3 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -17,19 +17,13 @@ package android.hardware; import static android.system.OsConstants.EACCES; -import static android.system.OsConstants.EBUSY; -import static android.system.OsConstants.EINVAL; import static android.system.OsConstants.ENODEV; -import static android.system.OsConstants.ENOSYS; -import static android.system.OsConstants.EOPNOTSUPP; -import static android.system.OsConstants.EUSERS; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.ActivityThread; import android.app.AppOpsManager; -import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.ImageFormat; @@ -47,7 +41,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSIllegalArgumentException; @@ -58,6 +51,7 @@ import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; @@ -284,14 +278,6 @@ public class Camera { */ public native static int getNumberOfCameras(); - private static final boolean sLandscapeToPortrait = - SystemProperties.getBoolean(CameraManager.LANDSCAPE_TO_PORTRAIT_PROP, false); - - private static boolean shouldOverrideToPortrait() { - return CompatChanges.isChangeEnabled(CameraManager.OVERRIDE_FRONT_CAMERA_APP_COMPAT) - && sLandscapeToPortrait; - } - /** * Returns the information about a particular camera. * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1. @@ -301,7 +287,8 @@ public class Camera { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { - boolean overrideToPortrait = shouldOverrideToPortrait(); + boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait( + ActivityThread.currentApplication().getApplicationContext()); _getCameraInfo(cameraId, overrideToPortrait, cameraInfo); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); @@ -498,9 +485,24 @@ public class Camera { mEventHandler = null; } - boolean overrideToPortrait = shouldOverrideToPortrait(); + boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait( + ActivityThread.currentApplication().getApplicationContext()); + boolean forceSlowJpegMode = shouldForceSlowJpegMode(); return native_setup(new WeakReference(this), cameraId, - ActivityThread.currentOpPackageName(), overrideToPortrait); + ActivityThread.currentOpPackageName(), overrideToPortrait, forceSlowJpegMode); + } + + private boolean shouldForceSlowJpegMode() { + Context applicationContext = ActivityThread.currentApplication().getApplicationContext(); + String[] slowJpegPackageNames = applicationContext.getResources().getStringArray( + R.array.config_forceSlowJpegModeList); + String callingPackageName = applicationContext.getPackageName(); + for (String packageName : slowJpegPackageNames) { + if (TextUtils.equals(packageName, callingPackageName)) { + return true; + } + } + return false; } /** used by Camera#open, Camera#open(int) */ @@ -571,7 +573,7 @@ public class Camera { @UnsupportedAppUsage private native int native_setup(Object cameraThis, int cameraId, String packageName, - boolean overrideToPortrait); + boolean overrideToPortrait, boolean forceSlowJpegMode); private native final void native_release(); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 10a7538cf4880ec23232474ded4ddf4ba3394fb1..2f81e0c118d8b85c6be01b50aad49f18b2253de5 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -880,8 +880,8 @@ public abstract class CameraDevice implements AutoCloseable { * {@code PRIV}{@code PREVIEW}{@code PREVIEW} {@code YUV / PRIV}{@code s1440p}{@code VIDEO_CALL} Preview with video call * {@code YUV / PRIV}{@code s1440p}{@code PREVIEW_VIDEO_STILL} {@code YUV / JPEG}{@code MAXIMUM}{@code STILL_CAPTURE} Multi-purpose stream with JPEG or YUV still capture * {@code YUV}{@code PREVIEW}{@code STILL_CAPTURE} {@code JPEG}{@code MAXIMUM}{@code STILL_CAPTURE} YUV and JPEG concurrent still image capture (for testing) - * {@code PRIV}{@code PREVIEW}{@code PREVIEW} {@code YUV / PRIV}{@code RECORD}{@code VIDEO_RECORD} {@code YUV / JPEG}{@code RECORD}{@code STILL_CAPTURE} Preview, video record and JPEG or YUV video snapshot - * {@code PRIV}{@code PREVIEW}{@code PREVIEW} {@code YUV}{@code PREVIEW}{@code PREVIEW} {@code YUV / JPEG}{@code MAXIMUM}{@code STILL_CAPTURE} Preview, in-application image processing, and JPEG or YUV still image capture + * {@code PRIV}{@code PREVIEW}{@code PREVIEW} {@code YUV / PRIV}{@code RECORD}{@code VIDEO_RECORD} {@code JPEG}{@code RECORD}{@code STILL_CAPTURE} Preview, video record and JPEG video snapshot + * {@code PRIV}{@code PREVIEW}{@code PREVIEW} {@code YUV}{@code PREVIEW}{@code PREVIEW} {@code JPEG}{@code MAXIMUM}{@code STILL_CAPTURE} Preview, in-application image processing, and JPEG still image capture *
*

* diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index ed1e9e5f62285e369e693bfa18cc72fee34a7bd5..3b2dd5ebdfc67e4885c48cccd2a806105672cab8 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -115,8 +115,14 @@ public final class CameraManager { @ChangeId @Overridable @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE) - @TestApi - public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; + public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L; + + /** + * Package-level opt in/out for the above. + * @hide + */ + public static final String PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT = + "android.camera.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT"; /** * System property for allowing the above @@ -392,6 +398,23 @@ public final class CameraManager { * except that it uses {@link java.util.concurrent.Executor} as an argument * instead of {@link android.os.Handler}.

* + *

Note: If the order between some availability callbacks matters, the implementation of the + * executor should handle those callbacks in the same thread to maintain the callbacks' order. + * Some examples are:

+ * + *
    + * + *
  • {@link AvailabilityCallback#onCameraAvailable} and + * {@link AvailabilityCallback#onCameraUnavailable} of the same camera ID.
  • + * + *
  • {@link AvailabilityCallback#onCameraAvailable} or + * {@link AvailabilityCallback#onCameraUnavailable} of a logical multi-camera, and {@link + * AvailabilityCallback#onPhysicalCameraUnavailable} or + * {@link AvailabilityCallback#onPhysicalCameraAvailable} of its physical + * cameras.
  • + * + *
+ * * @param executor The executor which will be used to invoke the callback. * @param callback the new callback to send camera availability notices to * @@ -608,7 +631,7 @@ public final class CameraManager { try { Size displaySize = getDisplaySize(); - boolean overrideToPortrait = shouldOverrideToPortrait(); + boolean overrideToPortrait = shouldOverrideToPortrait(mContext); CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId, mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait); try { @@ -728,7 +751,7 @@ public final class CameraManager { "Camera service is currently unavailable"); } - boolean overrideToPortrait = shouldOverrideToPortrait(); + boolean overrideToPortrait = shouldOverrideToPortrait(mContext); cameraUser = cameraService.connectDevice(callbacks, cameraId, mContext.getOpPackageName(), mContext.getAttributionTag(), uid, oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion, @@ -1160,9 +1183,26 @@ public final class CameraManager { return CameraManagerGlobal.get().getTorchStrengthLevel(cameraId); } - private static boolean shouldOverrideToPortrait() { - return CompatChanges.isChangeEnabled(OVERRIDE_FRONT_CAMERA_APP_COMPAT) - && CameraManagerGlobal.sLandscapeToPortrait; + /** + * @hide + */ + public static boolean shouldOverrideToPortrait(@Nullable Context context) { + if (!CameraManagerGlobal.sLandscapeToPortrait) { + return false; + } + + if (context != null) { + PackageManager packageManager = context.getPackageManager(); + + try { + return packageManager.getProperty(PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT, + context.getOpPackageName()).getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + // No such property + } + } + + return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT); } /** @@ -2319,6 +2359,15 @@ public final class CameraManager { final AvailabilityCallback callback = mCallbackMap.keyAt(i); postSingleUpdate(callback, executor, id, null /*physicalId*/, status); + + // Send the NOT_PRESENT state for unavailable physical cameras + if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) { + ArrayList unavailableIds = mUnavailablePhysicalDevices.get(id); + for (String unavailableId : unavailableIds) { + postSingleUpdate(callback, executor, id, unavailableId, + ICameraServiceListener.STATUS_NOT_PRESENT); + } + } } } // onStatusChangedLocked @@ -2338,9 +2387,8 @@ public final class CameraManager { } //TODO: Do we need to treat this as error? - if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id)) - || !mUnavailablePhysicalDevices.containsKey(id)) { - Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera " + if (!mDeviceStatus.containsKey(id) || !mUnavailablePhysicalDevices.containsKey(id)) { + Log.e(TAG, String.format("Camera %s is not present. Ignore physical camera " + "status change", id)); return; } @@ -2365,6 +2413,12 @@ public final class CameraManager { return; } + if (!isAvailable(mDeviceStatus.get(id))) { + Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera " + + "status change callback(s)", id)); + return; + } + final int callbackCount = mCallbackMap.size(); for (int i = 0; i < callbackCount; i++) { Executor executor = mCallbackMap.valueAt(i); diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl index 34d016adbc0654b050478370c13ae70b2cb2d475..7c54a9b01ddedea7bc0516bff0b60a3fe39eadad 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl +++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl @@ -36,4 +36,5 @@ parcelable CameraOutputConfig int surfaceGroupId; String physicalCameraId; List sharedSurfaceConfigs; + boolean isMultiResolutionOutput; } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 41370e37f0924650e23b90362c5c22975605932b..02e7f6036b3431db24af67c60ab2c291f8918acd 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -228,6 +228,9 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId, outputSurface); + if (output.isMultiResolutionOutput) { + cameraOutput.setMultiResolutionOutput(); + } if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) { cameraOutput.enableSurfaceSharing(); for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 9179f5de1a4f37e89e3ae09a6e74f73ec38f69d7..96472f4bad17464f13b95fa7c2b32327ac8ed102 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -87,6 +87,7 @@ public class CameraDeviceImpl extends CameraDevice // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) private ICameraDeviceUserWrapper mRemoteDevice; + private boolean mRemoteDeviceInit = false; // Lock to synchronize cross-thread access to device public interface final Object mInterfaceLock = new Object(); // access from this class and Session only! @@ -338,6 +339,8 @@ public class CameraDeviceImpl extends CameraDevice mDeviceExecutor.execute(mCallOnOpened); mDeviceExecutor.execute(mCallOnUnconfigured); + + mRemoteDeviceInit = true; } } @@ -1754,8 +1757,8 @@ public class CameraDeviceImpl extends CameraDevice } synchronized(mInterfaceLock) { - if (mRemoteDevice == null) { - return; // Camera already closed + if (mRemoteDevice == null && mRemoteDeviceInit) { + return; // Camera already closed, user is not interested in errors anymore. } // Redirect device callback to the offline session in case we are in the middle diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 956a474401bae346b5d49f6ded0a12d14ce932cc..d64009c734017ed6644ef40800152d8807f4d1cf 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -1220,14 +1220,6 @@ public final class MandatoryStreamCombination { new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD, STREAM_USE_CASE_STILL_CAPTURE)}, "Preview, video record and JPEG video snapshot"), - new StreamCombinationTemplate(new StreamTemplate [] { - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, - STREAM_USE_CASE_PREVIEW), - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD, - STREAM_USE_CASE_RECORD), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, - STREAM_USE_CASE_STILL_CAPTURE)}, - "Preview, video record and YUV video snapshot"), new StreamCombinationTemplate(new StreamTemplate [] { new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, STREAM_USE_CASE_PREVIEW), @@ -1236,14 +1228,6 @@ public final class MandatoryStreamCombination { new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD, STREAM_USE_CASE_STILL_CAPTURE)}, "Preview, in-application video processing and JPEG video snapshot"), - new StreamCombinationTemplate(new StreamTemplate [] { - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, - STREAM_USE_CASE_PREVIEW), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, - STREAM_USE_CASE_RECORD), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, - STREAM_USE_CASE_STILL_CAPTURE)}, - "Preview, in-application video processing and YUV video snapshot"), new StreamCombinationTemplate(new StreamTemplate [] { new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, STREAM_USE_CASE_PREVIEW), @@ -1252,14 +1236,6 @@ public final class MandatoryStreamCombination { new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, STREAM_USE_CASE_STILL_CAPTURE)}, "Preview, in-application image processing, and JPEG still image capture"), - new StreamCombinationTemplate(new StreamTemplate [] { - new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, - STREAM_USE_CASE_PREVIEW), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, - STREAM_USE_CASE_PREVIEW), - new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, - STREAM_USE_CASE_STILL_CAPTURE)}, - "Preview, in-application image processing, and YUV still image capture"), }; private static StreamCombinationTemplate sPreviewStabilizedStreamCombinations[] = { diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 558bb6335dd3d61d4239577fcefe355f8c3b7938..e3d650248ff8a6d46a569b6c2b68e37c54305a3a 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -421,7 +421,7 @@ public final class OutputConfiguration implements Parcelable { * call, or no non-negative group ID has been set. * @hide */ - void setMultiResolutionOutput() { + public void setMultiResolutionOutput() { if (mIsShared) { throw new IllegalStateException("Multi-resolution output flag must not be set for " + "configuration with surface sharing"); diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index dba1a5e8dfc616135e2b85aab27c349a802c53fb..6a667fe39974d16e0b1322be865a2fbde2b382b1 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -251,6 +251,10 @@ public final class DeviceStateManager { @Nullable private Boolean lastResult; + public FoldStateListener(Context context) { + this(context, folded -> {}); + } + public FoldStateListener(Context context, Consumer listener) { mFoldedDeviceStates = context.getResources().getIntArray( com.android.internal.R.array.config_foldedDeviceStates); @@ -266,5 +270,10 @@ public final class DeviceStateManager { mDelegate.accept(folded); } } + + @Nullable + public Boolean getFolded() { + return lastResult; + } } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 63dc7c7ed661516eccb659982aca3aee94483b30..aef2ae26747d2420a71ccbc5bc9d8803e14ac69c 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -40,6 +40,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -1006,7 +1007,8 @@ public final class DisplayManagerGlobal { @Override public void onDisplayEvent(int displayId, @DisplayEvent int event) { if (DEBUG) { - Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); + Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + eventToString( + event)); } handleDisplayEvent(displayId, event); } @@ -1040,6 +1042,12 @@ public final class DisplayManagerGlobal { @Override public void handleMessage(Message msg) { + if (DEBUG) { + Trace.beginSection( + "DisplayListenerDelegate(" + eventToString(msg.what) + + ", display=" + msg.arg1 + + ", listener=" + mListener.getClass() + ")"); + } switch (msg.what) { case EVENT_DISPLAY_ADDED: if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) { @@ -1066,6 +1074,9 @@ public final class DisplayManagerGlobal { } break; } + if (DEBUG) { + Trace.endSection(); + } } } @@ -1172,4 +1183,18 @@ public final class DisplayManagerGlobal { updateCallbackIfNeededLocked(); } } + + private static String eventToString(@DisplayEvent int event) { + switch (event) { + case EVENT_DISPLAY_ADDED: + return "ADDED"; + case EVENT_DISPLAY_CHANGED: + return "CHANGED"; + case EVENT_DISPLAY_REMOVED: + return "REMOVED"; + case EVENT_DISPLAY_BRIGHTNESS_CHANGED: + return "BRIGHTNESS_CHANGED"; + } + return "UNKNOWN"; + } } diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl index 9c2aa66993347f591c2de0f9eaf805c3b3a69fda..a36ccf6150bae83ee6f6f9c7dac11814f4c0b59e 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl @@ -39,5 +39,15 @@ oneway interface IUdfpsHbmListener { * {@link android.view.Display#getDisplayId()}. */ void onHbmDisabled(int displayId); + + /** + * To avoid delay in switching refresh rate when activating LHBM, allow screens to request + * higher refersh rate if auth is possible on particular screen + * + * @param displayId The displayId for which the refresh rate should be unset. See + * {@link android.view.Display#getDisplayId()}. + * @param isPossible If authentication is possible on particualr screen + */ + void onAuthenticationPossible(int displayId, boolean isPossible); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index baf8836c5af584ec241ec998d5ecb9f02ab947c4..b6cd06ea8b5cf423ecfc3b1054aef8217a13ef4f 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -168,7 +168,7 @@ public final class InputManager { * The android:label attribute specifies a human-readable descriptive * label to describe the keyboard layout in the user interface, such as "English (US)". * The android:keyboardLayout attribute refers to a - * + * * key character map resource that defines the keyboard layout. *

*/ diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 3d5c34c384314327793894d66bc4bf2340c60490..4f49f12691d0b1508a7361e9bd97ef6f1f2c7320 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -649,8 +649,11 @@ public abstract class BatteryStats implements Parcelable { return Uid.PROCESS_STATE_NONEXISTENT; } else if (procState == ActivityManager.PROCESS_STATE_TOP) { return Uid.PROCESS_STATE_TOP; - } else if (ActivityManager.isForegroundService(procState)) { - // State when app has put itself in the foreground. + } else if (procState == ActivityManager.PROCESS_STATE_BOUND_TOP) { + return Uid.PROCESS_STATE_BACKGROUND; + } else if (procState == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + return Uid.PROCESS_STATE_FOREGROUND_SERVICE; + } else if (procState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { return Uid.PROCESS_STATE_FOREGROUND_SERVICE; } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { // Persistent and other foreground states go here. @@ -2804,6 +2807,15 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getMobileRadioMeasuredBatteryConsumptionUC(); + /** + * Returns the battery consumption (in microcoulombs) of the phone calls, derived from on device + * power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getPhoneEnergyConsumptionUC(); + /** * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on * device power measurement data. diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index e1d15defad3812c378f568c123aa71900af486f6..125bdaf07b9083957684d7576e1952884f46ab10 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -74,6 +74,7 @@ interface IUserManager { String getUserAccount(int userId); void setUserAccount(int userId, String accountName); long getUserCreationTime(int userId); + int getUserSwitchability(int userId); boolean isUserSwitcherEnabled(int mUserId); boolean isRestricted(int userId); boolean canHaveRestrictedProfile(int userId); diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 320b02c2a0b415415df8920643e5250b37f21c8b..bc65061ba244f4baed5bd5f7b557af97f3c5ac23 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -110,7 +110,8 @@ public final class Trace { public static final long TRACE_TAG_THERMAL = 1L << 27; private static final long TRACE_TAG_NOT_READY = 1L << 63; - private static final int MAX_SECTION_NAME_LEN = 127; + /** @hide **/ + public static final int MAX_SECTION_NAME_LEN = 127; // Must be volatile to avoid word tearing. // This is only kept in case any apps get this by reflection but do not diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 91d231eb1c398c8808a45c970b063f8c07d507bc..787b609ab2c14bbaf9710a1916523476784a57d1 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -51,8 +51,7 @@ public final class UidBatteryConsumer extends BatteryConsumer { } /** - * The state of an application when it is either running a foreground (top) activity - * or a foreground service. + * The state of an application when it is either running a foreground (top) activity. */ public static final int STATE_FOREGROUND = 0; @@ -64,7 +63,8 @@ public final class UidBatteryConsumer extends BatteryConsumer { * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, - * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}. + * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}, + * {@link android.app.ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE}. */ public static final int STATE_BACKGROUND = 1; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e7641d8084ccca466d0aee20ae243b3faf233d20..83a4e9a8e6a89fe6340ed0703eeb683acdc812aa 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -58,7 +58,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.AndroidException; import android.util.ArraySet; import android.util.Log; @@ -595,8 +594,11 @@ public class UserManager { /** * Specifies if a user is disallowed from transferring files over USB. * - *

This restriction can only be set by a device owner, a profile owner on the primary - * user or a profile owner of an organization-owned managed profile on the parent profile. + *

This restriction can only be set by a + * device owner or a + * profile owner on the primary user's profile or a profile owner of an organization-owned + * + * managed profile on the parent profile. * When it is set by a device owner, it applies globally. When it is set by a profile owner * on the primary user or by a profile owner of an organization-owned managed profile on * the parent profile, it disables the primary user from transferring files over USB. No other @@ -1712,7 +1714,7 @@ public class UserManager { public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 1 << 2; /** - * Result returned in {@link #getUserSwitchability()} indicating user swichability. + * Result returned in {@link #getUserSwitchability()} indicating user switchability. * @hide */ @Retention(RetentionPolicy.SOURCE) @@ -2079,25 +2081,16 @@ public class UserManager { * @hide */ @Deprecated - @RequiresPermission(allOf = { - Manifest.permission.READ_PHONE_STATE, - Manifest.permission.MANAGE_USERS}, // Can be INTERACT_ACROSS_USERS instead. - conditional = true) + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @UserHandleAware public boolean canSwitchUsers() { - boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0; - boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM); - boolean inCall = false; - TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); - if (telephonyManager != null) { - inCall = telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE; + try { + return mService.getUserSwitchability(mUserId) == SWITCHABILITY_STATUS_OK; + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } - boolean isUserSwitchDisallowed = hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId); - return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall - && !isUserSwitchDisallowed; } /** @@ -2131,34 +2124,14 @@ public class UserManager { * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable. * @hide */ - @RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE, - android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}) public @UserSwitchabilityResult int getUserSwitchability(UserHandle userHandle) { - final TelephonyManager tm = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - - int flags = SWITCHABILITY_STATUS_OK; - if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { - flags |= SWITCHABILITY_STATUS_USER_IN_CALL; - } - if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, userHandle)) { - flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; - } - - // System User is always unlocked in Headless System User Mode, so ignore this flag - if (!isHeadlessSystemUserMode()) { - final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0; - final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM); - - if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) { - flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED; - } + try { + return mService.getUserSwitchability(userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } - - return flags; } /** diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 08de87ebe2e61c4cd3407f13d3e33da224813dee..0d9f500cb1028ac6d20f9ca1ac8d0fde11d1ad40 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -2552,6 +2552,8 @@ public class StorageManager { * This API doesn't require any special permissions, though typical implementations * will require being called from an SELinux domain that allows setting file attributes * related to quota (eg the GID or project ID). + * If the calling user has MANAGE_EXTERNAL_STORAGE permissions, quota for shared profile's + * volumes is also updated. * * The default platform user of this API is the MediaProvider process, which is * responsible for managing all of external storage. @@ -2572,7 +2574,14 @@ public class StorageManager { @QuotaType int quotaType) throws IOException { long projectId; final String filePath = path.getCanonicalPath(); - final StorageVolume volume = getStorageVolume(path); + int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE; + // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also + // returned by enabling FLAG_INCLUDE_SHARED_PROFILE. + if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { + volFlags |= FLAG_INCLUDE_SHARED_PROFILE; + } + final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags); + final StorageVolume volume = getStorageVolume(availableVolumes, path); if (volume == null) { Log.w(TAG, "Failed to update quota type for " + filePath); return; diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 3bf9ca04414179efeb46980c40885ae92a18af7c..6f2a915cee46df8829c14bcb7dc2f138560d23f2 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -16,7 +16,9 @@ package android.preference; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.app.NotificationManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; @@ -35,6 +37,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.preference.VolumePreference.VolumeStore; +import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.System; @@ -44,6 +47,7 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import com.android.internal.annotations.GuardedBy; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.os.SomeArgs; import java.util.concurrent.TimeUnit; @@ -115,7 +119,6 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba private final int mMaxStreamVolume; private boolean mAffectedByRingerMode; private boolean mNotificationOrRing; - private final boolean mNotifAliasRing; private final Receiver mReceiver = new Receiver(); private Handler mHandler; @@ -138,12 +141,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba private int mRingerMode; private int mZenMode; private boolean mPlaySample; + private final boolean mDeviceHasProductStrategies; private static final int MSG_SET_STREAM_VOLUME = 0; private static final int MSG_START_SAMPLE = 1; private static final int MSG_STOP_SAMPLE = 2; private static final int MSG_INIT_SAMPLE = 3; + private static final int MSG_UPDATE_SLIDER_MAYBE_LATER = 4; private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000; + private static final int CHECK_UPDATE_SLIDER_LATER_MS = 500; private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500); private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500); private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000); @@ -158,6 +164,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba this(context, streamType, defaultUri, callback, true /* playSample */); } + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) public SeekBarVolumizer( Context context, int streamType, @@ -166,6 +173,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba boolean playSample) { mContext = context; mAudioManager = context.getSystemService(AudioManager.class); + mDeviceHasProductStrategies = hasAudioProductStrategies(); mNotificationManager = context.getSystemService(NotificationManager.class); mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy(); mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy @@ -180,11 +188,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (mNotificationOrRing) { mRingerMode = mAudioManager.getRingerModeInternal(); } - mNotifAliasRing = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_alias_ring_notif_stream_types); mZenMode = mNotificationManager.getZenMode(); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { mVolumeGroupId = getVolumeGroupIdForLegacyStreamType(mStreamType); mAttributes = getAudioAttributesForLegacyStreamType( mStreamType); @@ -211,6 +217,12 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mDefaultUri = defaultUri; } + /** + * DO NOT CALL every time this is needed, use once in constructor, + * read mDeviceHasProductStrategies instead + * @return true if stream types are used for volume management, false if volume groups are + * used for volume management + */ private boolean hasAudioProductStrategies() { return AudioManager.getAudioProductStrategies().size() > 0; } @@ -288,7 +300,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba * so that when user attempts to slide the notification seekbar out of vibrate the * seekbar doesn't wrongly snap back to 0 when the streams aren't aliased */ - if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false) + || mStreamType == AudioManager.STREAM_RING || (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) { mSeekBar.setProgress(0, true); } @@ -326,6 +340,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba onInitSample(); } break; + case MSG_UPDATE_SLIDER_MAYBE_LATER: + onUpdateSliderMaybeLater(); + break; default: Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what); } @@ -349,6 +366,21 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba : isDelay() ? START_SAMPLE_DELAY_MS : 0); } + private void onUpdateSliderMaybeLater() { + if (isDelay()) { + postUpdateSliderMaybeLater(); + return; + } + updateSlider(); + } + + private void postUpdateSliderMaybeLater() { + if (mHandler == null) return; + mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SLIDER_MAYBE_LATER), + CHECK_UPDATE_SLIDER_LATER_MS); + } + // After stop volume it needs to add a small delay when playing volume or set stream. // It is because the call volume is from the earpiece and the alarm/ring/media // is from the speaker. If play the alarm volume or set alarm stream right after stop @@ -365,7 +397,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba // set the time of stop volume if ((mStreamType == AudioManager.STREAM_VOICE_CALL || mStreamType == AudioManager.STREAM_RING - || (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION) + || (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false) + && mStreamType == AudioManager.STREAM_NOTIFICATION) || mStreamType == AudioManager.STREAM_ALARM)) { sStopVolumeTime = java.lang.System.currentTimeMillis(); } @@ -416,7 +450,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba postStopSample(); mContext.getContentResolver().unregisterContentObserver(mVolumeObserver); mReceiver.setListening(false); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { unregisterVolumeGroupCb(); } mSeekBar.setOnSeekBarChangeListener(null); @@ -436,7 +470,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]), false, mVolumeObserver); mReceiver.setListening(true); - if (hasAudioProductStrategies()) { + if (mDeviceHasProductStrategies) { registerVolumeGroupCb(); } } @@ -460,6 +494,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mLastProgress = progress; mHandler.removeMessages(MSG_SET_STREAM_VOLUME); mHandler.removeMessages(MSG_START_SAMPLE); + mHandler.removeMessages(MSG_UPDATE_SLIDER_MAYBE_LATER); mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME), isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0); } @@ -603,7 +638,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); - if (hasAudioProductStrategies() && !isDelay()) { + if (mDeviceHasProductStrategies && !isDelay()) { updateVolumeSlider(streamType, streamValue); } } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { @@ -615,9 +650,16 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); - if (hasAudioProductStrategies() && !isDelay()) { - int streamVolume = mAudioManager.getStreamVolume(streamType); - updateVolumeSlider(streamType, streamVolume); + + if (mDeviceHasProductStrategies) { + if (isDelay()) { + // not the right time to update the sliders, try again later + postUpdateSliderMaybeLater(); + } else { + int streamVolume = mAudioManager.getStreamVolume(streamType); + updateVolumeSlider(streamType, streamVolume); + } + } else { int volumeGroup = getVolumeGroupIdForLegacyStreamType(streamType); if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP @@ -644,8 +686,10 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } private void updateVolumeSlider(int streamType, int streamValue) { - final boolean streamMatch = mNotifAliasRing && mNotificationOrRing - ? isNotificationOrRing(streamType) : streamType == mStreamType; + final boolean streamMatch = !DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false) + && mNotificationOrRing ? isNotificationOrRing(streamType) : + streamType == mStreamType; if (mSeekBar != null && streamMatch && streamValue != -1) { final boolean muted = mAudioManager.isStreamMute(mStreamType) || streamValue == 0; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 038beabcf22fc0a1e708a36102863d75807c1d0f..7a757690b8687bd89a4cf0df8bd74a4c14e2608f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7999,6 +7999,15 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; + /** + * Flag that specifies whether font size has been changed. The flag will + * be set when users change the scaled value of font size for the first time. + * @hide + */ + @Readable + public static final String ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED = + "accessibility_font_scaling_has_been_changed"; + /** * Setting that specifies whether display color space adjustment is * enabled. @@ -9199,6 +9208,14 @@ public final class Settings { public static final String SCREENSAVER_COMPLICATIONS_ENABLED = "screensaver_complications_enabled"; + /** + * Whether home controls are enabled to be shown over the screensaver by the user. + * + * @hide + */ + public static final String SCREENSAVER_HOME_CONTROLS_ENABLED = + "screensaver_home_controls_enabled"; + /** * Default, indicates that the user has not yet started the dock setup flow. @@ -9231,6 +9248,14 @@ public final class Settings { */ public static final int DOCK_SETUP_PROMPTED = 3; + /** + * Indicates that the user has started dock setup but never finished it. + * One of the possible states for {@link #DOCK_SETUP_STATE}. + * + * @hide + */ + public static final int DOCK_SETUP_INCOMPLETE = 4; + /** * Indicates that the user has completed dock setup. * One of the possible states for {@link #DOCK_SETUP_STATE}. @@ -9239,6 +9264,14 @@ public final class Settings { */ public static final int DOCK_SETUP_COMPLETED = 10; + /** + * Indicates that dock setup timed out before the user could complete it. + * One of the possible states for {@link #DOCK_SETUP_STATE}. + * + * @hide + */ + public static final int DOCK_SETUP_TIMED_OUT = 11; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -9246,7 +9279,9 @@ public final class Settings { DOCK_SETUP_STARTED, DOCK_SETUP_PAUSED, DOCK_SETUP_PROMPTED, - DOCK_SETUP_COMPLETED + DOCK_SETUP_INCOMPLETE, + DOCK_SETUP_COMPLETED, + DOCK_SETUP_TIMED_OUT }) public @interface DockSetupState { } @@ -9515,7 +9550,7 @@ public final class Settings { /** * Indicates whether "seen" notifications should be suppressed from the lockscreen. *

- * Type: int (0 for false, 1 for true) + * Type: int (0 for unset, 1 for true, 2 for false) * * @hide */ @@ -9818,11 +9853,12 @@ public final class Settings { "fingerprint_side_fps_auth_downtime"; /** - * Whether or not a SFPS device is required to be interactive for auth to unlock the device. + * Whether or not a SFPS device is enabling the performant auth setting. + * The "_V2" suffix was added to re-introduce the default behavior for + * users. See b/265264294 fore more details. * @hide */ - public static final String SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED = - "sfps_require_screen_on_to_auth_enabled"; + public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled_v2"; /** * Whether or not debugging is enabled. @@ -9900,6 +9936,28 @@ public final class Settings { public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED = "active_unlock_on_unlock_intent_when_biometric_enrolled"; + /** + * If active unlock triggers on unlock intents, then also request active unlock on + * these wake-up reasons. See {@link PowerManager.WakeReason} for value mappings. + * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this + * setting should be disabled, then this should be set to an empty string. A null value + * will use the system default value (WAKE_REASON_UNFOLD_DEVICE). + * @hide + */ + public static final String ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS = + "active_unlock_wakeups_considered_unlock_intents"; + + /** + * If active unlock triggers and succeeds on these wakeups, force dismiss keyguard on + * these wake reasons. See {@link PowerManager#WakeReason} for value mappings. + * WakeReasons should be separated by a pipe. For example: "0|3" or "0". If this + * setting should be disabled, then this should be set to an empty string. A null value + * will use the system default value (WAKE_REASON_UNFOLD_DEVICE). + * @hide + */ + public static final String ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD = + "active_unlock_wakeups_to_force_dismiss_keyguard"; + /** * Whether the assist gesture should be enabled. * @@ -10938,21 +10996,46 @@ public final class Settings { public @interface DeviceStateRotationLockSetting { } + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_UNKNOWN = -1; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_FOLDED = 0; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_HALF_FOLDED = 1; + /** @hide */ + public static final int DEVICE_STATE_ROTATION_KEY_UNFOLDED = 2; + + /** + * The different postures that can be used as keys with + * {@link #DEVICE_STATE_ROTATION_LOCK}. + * @hide + */ + @IntDef(prefix = {"DEVICE_STATE_ROTATION_KEY_"}, value = { + DEVICE_STATE_ROTATION_KEY_UNKNOWN, + DEVICE_STATE_ROTATION_KEY_FOLDED, + DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, + DEVICE_STATE_ROTATION_KEY_UNFOLDED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeviceStateRotationLockKey { + } + /** * Rotation lock setting keyed on device state. * - * This holds a serialized map using int keys that represent Device States and value of + * This holds a serialized map using int keys that represent postures in + * {@link DeviceStateRotationLockKey} and value of * {@link DeviceStateRotationLockSetting} representing the rotation lock setting for that - * device state. + * posture. * * Serialized as key0:value0:key1:value1:...:keyN:valueN. * * Example: "0:1:1:2:2:1" * This example represents a map of: *

    - *
  • 0 -> DEVICE_STATE_ROTATION_LOCK_LOCKED
  • - *
  • 1 -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED
  • - *
  • 2 -> DEVICE_STATE_ROTATION_LOCK_IGNORED
  • + *
  • DEVICE_STATE_ROTATION_KEY_FOLDED -> DEVICE_STATE_ROTATION_LOCK_LOCKED
  • + *
  • DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED
  • + *
  • DEVICE_STATE_ROTATION_KEY_UNFOLDED -> DEVICE_STATE_ROTATION_LOCK_IGNORED
  • *
* * @hide @@ -11015,6 +11098,13 @@ public final class Settings { public static final String EXTRA_AUTOMATIC_POWER_SAVE_MODE = "extra_automatic_power_save_mode"; + /** + * Whether lockscreen weather is enabled. + * + * @hide + */ + public static final String LOCK_SCREEN_WEATHER_ENABLED = "lockscreen_weather_enabled"; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java index 4f37cd91b11fd015c46586a2960262c72e30e313..a2ffa5d34219f7a95553ffe69f726a7dc8b628b7 100644 --- a/core/java/android/service/appprediction/AppPredictionService.java +++ b/core/java/android/service/appprediction/AppPredictionService.java @@ -328,7 +328,7 @@ public abstract class AppPredictionService extends Service { Slog.e(TAG, "Callback is null, likely the binder has died."); return false; } - return mCallback.equals(callback); + return mCallback.asBinder().equals(callback.asBinder()); } public void destroy() { diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 0fb9f57f5f574380e3b180167a435317328ab735..b0e847cd53f94b9c7a81043420d059364c7000ae 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -166,7 +166,7 @@ public final class FillEventHistory implements Parcelable { } /** - * Description of an event that occured after the latest call to + * Description of an event that occurred after the latest call to * {@link FillCallback#onSuccess(FillResponse)}. */ public static final class Event { diff --git a/core/java/android/service/chooser/ChooserAction.java b/core/java/android/service/chooser/ChooserAction.java index 3010049633d41f37edf9310f51bd8f22d2cdee8b..a61b781b6bca20b8f67319e123d80d523256024d 100644 --- a/core/java/android/service/chooser/ChooserAction.java +++ b/core/java/android/service/chooser/ChooserAction.java @@ -27,10 +27,9 @@ import java.util.Objects; /** * A ChooserAction is an app-defined action that can be provided to the Android Sharesheet to - * be shown to the user when {@link android.content.Intent.ACTION_CHOOSER} is invoked. + * be shown to the user when {@link android.content.Intent#ACTION_CHOOSER} is invoked. * - * @see android.content.Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS - * @see android.content.Intent.EXTRA_CHOOSER_PAYLOAD_RESELECTION_ACTION + * @see android.content.Intent#EXTRA_CHOOSER_CUSTOM_ACTIONS * @hide */ public final class ChooserAction implements Parcelable { @@ -88,6 +87,7 @@ public final class ChooserAction implements Parcelable { return "ChooserAction {" + "label=" + mLabel + ", intent=" + mAction + "}"; } + @NonNull public static final Parcelable.Creator CREATOR = new Creator() { @Override @@ -137,6 +137,7 @@ public final class ChooserAction implements Parcelable { * object. * @return the built action */ + @NonNull public ChooserAction build() { return new ChooserAction(mIcon, mLabel, mAction); } diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java index a2fa1392b0796dddc82a1a758b6bf24402134a38..a3892238f1e6a789ebdc0397b5d9f94709a39198 100644 --- a/core/java/android/service/dreams/DreamActivity.java +++ b/core/java/android/service/dreams/DreamActivity.java @@ -58,11 +58,13 @@ public class DreamActivity extends Activity { setTitle(title); } - final Bundle extras = getIntent().getExtras(); - mCallback = (DreamService.DreamActivityCallbacks) extras.getBinder(EXTRA_CALLBACK); - - if (mCallback != null) { + final Object callback = getIntent().getExtras().getBinder(EXTRA_CALLBACK); + if (callback instanceof DreamService.DreamActivityCallbacks) { + mCallback = (DreamService.DreamActivityCallbacks) callback; mCallback.onActivityCreated(this); + } else { + mCallback = null; + finishAndRemoveTask(); } } diff --git a/core/java/android/service/dreams/DreamOverlayConnectionHandler.java b/core/java/android/service/dreams/DreamOverlayConnectionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..cafe02ad86580962feab13ef1e85d5aee2ab3fbb --- /dev/null +++ b/core/java/android/service/dreams/DreamOverlayConnectionHandler.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.dreams; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ObservableServiceConnection; +import com.android.internal.util.PersistentServiceConnection; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Handles the service connection to {@link IDreamOverlay} + * + * @hide + */ +@VisibleForTesting +public final class DreamOverlayConnectionHandler { + private static final String TAG = "DreamOverlayConnection"; + + private static final int MSG_ADD_CONSUMER = 1; + private static final int MSG_REMOVE_CONSUMER = 2; + private static final int MSG_OVERLAY_CLIENT_READY = 3; + + private final Handler mHandler; + private final PersistentServiceConnection mConnection; + // Retrieved Client + private IDreamOverlayClient mClient; + // A list of pending requests to execute on the overlay. + private final List> mConsumers = new ArrayList<>(); + private final OverlayConnectionCallback mCallback; + + DreamOverlayConnectionHandler( + Context context, + Looper looper, + Intent serviceIntent, + int minConnectionDurationMs, + int maxReconnectAttempts, + int baseReconnectDelayMs) { + this(context, looper, serviceIntent, minConnectionDurationMs, maxReconnectAttempts, + baseReconnectDelayMs, new Injector()); + } + + @VisibleForTesting + public DreamOverlayConnectionHandler( + Context context, + Looper looper, + Intent serviceIntent, + int minConnectionDurationMs, + int maxReconnectAttempts, + int baseReconnectDelayMs, + Injector injector) { + mCallback = new OverlayConnectionCallback(); + mHandler = new Handler(looper, new OverlayHandlerCallback()); + mConnection = injector.buildConnection( + context, + mHandler, + serviceIntent, + minConnectionDurationMs, + maxReconnectAttempts, + baseReconnectDelayMs + ); + } + + /** + * Bind to the overlay service. If binding fails, we automatically call unbind to clean + * up resources. + * + * @return true if binding was successful, false otherwise. + */ + public boolean bind() { + mConnection.addCallback(mCallback); + final boolean success = mConnection.bind(); + if (!success) { + unbind(); + } + return success; + } + + /** + * Unbind from the overlay service, clearing any pending callbacks. + */ + public void unbind() { + mConnection.removeCallback(mCallback); + // Remove any pending messages. + mHandler.removeCallbacksAndMessages(null); + mClient = null; + mConsumers.clear(); + mConnection.unbind(); + } + + /** + * Adds a consumer to run once the overlay service has connected. If the overlay service + * disconnects (eg binding dies) and then reconnects, this consumer will be re-run unless + * removed. + * + * @param consumer The consumer to run. This consumer is always executed asynchronously. + */ + public void addConsumer(Consumer consumer) { + final Message msg = mHandler.obtainMessage(MSG_ADD_CONSUMER, consumer); + mHandler.sendMessage(msg); + } + + /** + * Removes the consumer, preventing this consumer from being called again. + * + * @param consumer The consumer to remove. + */ + public void removeConsumer(Consumer consumer) { + final Message msg = mHandler.obtainMessage(MSG_REMOVE_CONSUMER, consumer); + mHandler.sendMessage(msg); + // Clear any pending messages to add this consumer + mHandler.removeMessages(MSG_ADD_CONSUMER, consumer); + } + + private final class OverlayHandlerCallback implements Handler.Callback { + @Override + public boolean handleMessage(@NonNull Message msg) { + switch (msg.what) { + case MSG_OVERLAY_CLIENT_READY: + onOverlayClientReady((IDreamOverlayClient) msg.obj); + break; + case MSG_ADD_CONSUMER: + onAddConsumer((Consumer) msg.obj); + break; + case MSG_REMOVE_CONSUMER: + onRemoveConsumer((Consumer) msg.obj); + break; + } + return true; + } + } + + private void onOverlayClientReady(IDreamOverlayClient client) { + mClient = client; + for (Consumer consumer : mConsumers) { + consumer.accept(mClient); + } + } + + private void onAddConsumer(Consumer consumer) { + if (mClient != null) { + consumer.accept(mClient); + } + mConsumers.add(consumer); + } + + private void onRemoveConsumer(Consumer consumer) { + mConsumers.remove(consumer); + } + + private final class OverlayConnectionCallback implements + ObservableServiceConnection.Callback { + + private final IDreamOverlayClientCallback mClientCallback = + new IDreamOverlayClientCallback.Stub() { + @Override + public void onDreamOverlayClient(IDreamOverlayClient client) { + final Message msg = + mHandler.obtainMessage(MSG_OVERLAY_CLIENT_READY, client); + mHandler.sendMessage(msg); + } + }; + + @Override + public void onConnected( + ObservableServiceConnection connection, + IDreamOverlay service) { + try { + service.getClient(mClientCallback); + } catch (RemoteException e) { + Log.e(TAG, "could not get DreamOverlayClient", e); + } + } + + @Override + public void onDisconnected(ObservableServiceConnection connection, + int reason) { + mClient = null; + // Cancel any pending messages about the overlay being ready, since it is no + // longer ready. + mHandler.removeMessages(MSG_OVERLAY_CLIENT_READY); + } + } + + /** + * Injector for testing + */ + @VisibleForTesting + public static class Injector { + /** + * Returns milliseconds since boot, not counting time spent in deep sleep. Can be overridden + * in tests with a fake clock. + */ + public PersistentServiceConnection buildConnection( + Context context, + Handler handler, + Intent serviceIntent, + int minConnectionDurationMs, + int maxReconnectAttempts, + int baseReconnectDelayMs) { + final Executor executor = handler::post; + final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + return new PersistentServiceConnection<>( + context, + executor, + handler, + IDreamOverlay.Stub::asInterface, + serviceIntent, + flags, + minConnectionDurationMs, + maxReconnectAttempts, + baseReconnectDelayMs + ); + } + } +} diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 6e8198ba0cd1a460c9cb4dfa0c63ed764f8c1911..5469916bea4e03480767141ae45584aeea114a87 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -27,6 +27,8 @@ import android.os.RemoteException; import android.util.Log; import android.view.WindowManager; +import java.util.concurrent.Executor; + /** * Basic implementation of for {@link IDreamOverlay} for testing. @@ -36,37 +38,138 @@ import android.view.WindowManager; public abstract class DreamOverlayService extends Service { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = false; - private boolean mShowComplications; - private ComponentName mDreamComponent; - private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { + // The last client that started dreaming and hasn't ended + private OverlayClient mCurrentClient; + + /** + * Executor used to run callbacks that subclasses will implement. Any calls coming over Binder + * from {@link OverlayClient} should perform the work they need to do on this executor. + */ + private Executor mExecutor; + + // An {@link IDreamOverlayClient} implementation that identifies itself when forwarding + // requests to the {@link DreamOverlayService} + private static class OverlayClient extends IDreamOverlayClient.Stub { + private final DreamOverlayService mService; + private boolean mShowComplications; + private ComponentName mDreamComponent; + IDreamOverlayCallback mDreamOverlayCallback; + + OverlayClient(DreamOverlayService service) { + mService = service; + } + @Override - public void startDream(WindowManager.LayoutParams layoutParams, - IDreamOverlayCallback callback, String dreamComponent, - boolean shouldShowComplications) { - mDreamOverlayCallback = callback; + public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback, + String dreamComponent, boolean shouldShowComplications) throws RemoteException { mDreamComponent = ComponentName.unflattenFromString(dreamComponent); mShowComplications = shouldShowComplications; - onStartDream(layoutParams); + mDreamOverlayCallback = callback; + mService.startDream(this, params); } @Override public void wakeUp() { - onWakeUp(() -> { + mService.wakeUp(this, () -> { try { mDreamOverlayCallback.onWakeUpComplete(); } catch (RemoteException e) { - Log.e(TAG, "Could not notify dream of wakeUp:" + e); + Log.e(TAG, "Could not notify dream of wakeUp", e); } }); } - }; - IDreamOverlayCallback mDreamOverlayCallback; + @Override + public void endDream() { + mService.endDream(this); + } + + private void onExitRequested() { + try { + mDreamOverlayCallback.onExitRequested(); + } catch (RemoteException e) { + Log.e(TAG, "Could not request exit:" + e); + } + } + + private boolean shouldShowComplications() { + return mShowComplications; + } + + private ComponentName getComponent() { + return mDreamComponent; + } + } + + private void startDream(OverlayClient client, WindowManager.LayoutParams params) { + // Run on executor as this is a binder call from OverlayClient. + mExecutor.execute(() -> { + endDreamInternal(mCurrentClient); + mCurrentClient = client; + onStartDream(params); + }); + } + + private void endDream(OverlayClient client) { + // Run on executor as this is a binder call from OverlayClient. + mExecutor.execute(() -> endDreamInternal(client)); + } + + private void endDreamInternal(OverlayClient client) { + if (client == null || client != mCurrentClient) { + return; + } + + onEndDream(); + mCurrentClient = null; + } + + private void wakeUp(OverlayClient client, Runnable callback) { + // Run on executor as this is a binder call from OverlayClient. + mExecutor.execute(() -> { + if (mCurrentClient != client) { + return; + } + + onWakeUp(callback); + }); + } + + private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { + @Override + public void getClient(IDreamOverlayClientCallback callback) { + try { + callback.onDreamOverlayClient( + new OverlayClient(DreamOverlayService.this)); + } catch (RemoteException e) { + Log.e(TAG, "could not send client to callback", e); + } + } + }; public DreamOverlayService() { } + /** + * This constructor allows providing an executor to run callbacks on. + * + * @hide + */ + public DreamOverlayService(@NonNull Executor executor) { + mExecutor = executor; + } + + @Override + public void onCreate() { + super.onCreate(); + if (mExecutor == null) { + // If no executor was provided, use the main executor. onCreate is the earliest time + // getMainExecutor is available. + mExecutor = getMainExecutor(); + } + } + @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { @@ -76,6 +179,10 @@ public abstract class DreamOverlayService extends Service { /** * This method is overridden by implementations to handle when the dream has started and the * window is ready to be interacted with. + * + * This callback will be run on the {@link Executor} provided in the constructor if provided, or + * on the main executor if none was provided. + * * @param layoutParams The {@link android.view.WindowManager.LayoutParams} associated with the * dream window. */ @@ -83,31 +190,51 @@ public abstract class DreamOverlayService extends Service { /** * This method is overridden by implementations to handle when the dream has been requested - * to wakeup. This allows any overlay animations to run. + * to wakeup. This allows any overlay animations to run. By default, the method will invoke + * the callback immediately. + * + * This callback will be run on the {@link Executor} provided in the constructor if provided, or + * on the main executor if none was provided. * * @param onCompleteCallback The callback to trigger to notify the dream service that the * overlay has completed waking up. * @hide */ public void onWakeUp(@NonNull Runnable onCompleteCallback) { + onCompleteCallback.run(); + } + + /** + * This method is overridden by implementations to handle when the dream has ended. There may + * be earlier signals leading up to this step, such as @{@link #onWakeUp(Runnable)}. + * + * This callback will be run on the {@link Executor} provided in the constructor if provided, or + * on the main executor if none was provided. + */ + public void onEndDream() { } /** * This method is invoked to request the dream exit. */ public final void requestExit() { - try { - mDreamOverlayCallback.onExitRequested(); - } catch (RemoteException e) { - Log.e(TAG, "Could not request exit:" + e); + if (mCurrentClient == null) { + throw new IllegalStateException("requested exit with no dream present"); } + + mCurrentClient.onExitRequested(); } /** * Returns whether to show complications on the dream overlay. */ public final boolean shouldShowComplications() { - return mShowComplications; + if (mCurrentClient == null) { + throw new IllegalStateException( + "requested if should show complication when no dream active"); + } + + return mCurrentClient.shouldShowComplications(); } /** @@ -115,6 +242,10 @@ public abstract class DreamOverlayService extends Service { * @hide */ public final ComponentName getDreamComponent() { - return mDreamComponent; + if (mCurrentClient == null) { + throw new IllegalStateException("requested dream component when no dream active"); + } + + return mCurrentClient.getComponent(); } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 8b9852a8f1b7963a94b957944a2f13a0ab114c0e..9107c5f4bbdbeba374521ebe6aea0c6ca4243bb0 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -68,8 +68,6 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.R; import com.android.internal.util.DumpUtils; -import com.android.internal.util.ObservableServiceConnection; -import com.android.internal.util.PersistentServiceConnection; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -77,8 +75,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -245,68 +241,7 @@ public class DreamService extends Service implements Window.Callback { private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; - private OverlayConnection mOverlayConnection; - - private static class OverlayConnection extends PersistentServiceConnection { - // Overlay set during onBind. - private IDreamOverlay mOverlay; - // A list of pending requests to execute on the overlay. - private final ArrayList> mConsumers = new ArrayList<>(); - - private final Callback mCallback = new Callback() { - @Override - public void onConnected(ObservableServiceConnection connection, - IDreamOverlay service) { - mOverlay = service; - for (Consumer consumer : mConsumers) { - consumer.accept(mOverlay); - } - } - - @Override - public void onDisconnected(ObservableServiceConnection connection, - int reason) { - mOverlay = null; - } - }; - - OverlayConnection(Context context, - Executor executor, - Handler handler, - ServiceTransformer transformer, - Intent serviceIntent, - int flags, - int minConnectionDurationMs, - int maxReconnectAttempts, - int baseReconnectDelayMs) { - super(context, executor, handler, transformer, serviceIntent, flags, - minConnectionDurationMs, - maxReconnectAttempts, baseReconnectDelayMs); - } - - @Override - public boolean bind() { - addCallback(mCallback); - return super.bind(); - } - - @Override - public void unbind() { - removeCallback(mCallback); - super.unbind(); - } - - public void addConsumer(Consumer consumer) { - mConsumers.add(consumer); - if (mOverlay != null) { - consumer.accept(mOverlay); - } - } - - public void removeConsumer(Consumer consumer) { - mConsumers.remove(consumer); - } - } + private DreamOverlayConnectionHandler mOverlayConnection; private final IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() { @Override @@ -1009,18 +944,18 @@ public class DreamService extends Service implements Window.Callback { final Resources resources = getResources(); final Intent overlayIntent = new Intent().setComponent(overlayComponent); - mOverlayConnection = new OverlayConnection( + mOverlayConnection = new DreamOverlayConnectionHandler( /* context= */ this, - getMainExecutor(), - mHandler, - IDreamOverlay.Stub::asInterface, + Looper.getMainLooper(), overlayIntent, - /* flags= */ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, resources.getInteger(R.integer.config_minDreamOverlayDurationMs), resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts), resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs)); - mOverlayConnection.bind(); + if (!mOverlayConnection.bind()) { + // Binding failed. + mOverlayConnection = null; + } } return mDreamServiceWrapper; @@ -1031,6 +966,7 @@ public class DreamService extends Service implements Window.Callback { // We must unbind from any overlay connection if we are unbound before finishing. if (mOverlayConnection != null) { mOverlayConnection.unbind(); + mOverlayConnection = null; } return super.onUnbind(intent); @@ -1044,6 +980,23 @@ public class DreamService extends Service implements Window.Callback { *

*/ public final void finish() { + // If there is an active overlay connection, signal that the dream is ending before + // continuing. Note that the overlay cannot rely on the unbound state, since another dream + // might have bound to it in the meantime. + if (mOverlayConnection != null) { + mOverlayConnection.addConsumer(overlay -> { + try { + overlay.endDream(); + mOverlayConnection.unbind(); + mOverlayConnection = null; + finish(); + } catch (RemoteException e) { + Log.e(mTag, "could not inform overlay of dream end:" + e); + } + }); + return; + } + if (mDebug) Slog.v(mTag, "finish(): mFinished=" + mFinished); Activity activity = mActivity; @@ -1060,10 +1013,6 @@ public class DreamService extends Service implements Window.Callback { } mFinished = true; - if (mOverlayConnection != null) { - mOverlayConnection.unbind(); - } - if (mDreamToken == null) { if (mDebug) Slog.v(mTag, "finish() called when not attached."); stopSelf(); @@ -1266,9 +1215,10 @@ public class DreamService extends Service implements Window.Callback { * Must run on mHandler. * * @param dreamToken Token for this dream service. - * @param started A callback that will be invoked once onDreamingStarted has completed. + * @param started A callback that will be invoked once onDreamingStarted has completed. */ - private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) { + private void attach(IBinder dreamToken, boolean canDoze, boolean isPreviewMode, + IRemoteCallback started) { if (mDreamToken != null) { Slog.e(mTag, "attach() called when dream with token=" + mDreamToken + " already attached"); @@ -1316,12 +1266,18 @@ public class DreamService extends Service implements Window.Callback { i.putExtra(DreamActivity.EXTRA_CALLBACK, new DreamActivityCallbacks(mDreamToken)); final ServiceInfo serviceInfo = fetchServiceInfo(this, new ComponentName(this, getClass())); - i.putExtra(DreamActivity.EXTRA_DREAM_TITLE, fetchDreamLabel(this, serviceInfo)); + i.putExtra(DreamActivity.EXTRA_DREAM_TITLE, + fetchDreamLabel(this, serviceInfo, isPreviewMode)); try { if (!ActivityTaskManager.getService().startDreamActivity(i)) { detach(); } + } catch (SecurityException e) { + Log.w(mTag, + "Received SecurityException trying to start DreamActivity. " + + "Aborting dream start."); + detach(); } catch (RemoteException e) { Log.w(mTag, "Could not connect to activity task manager to start dream activity"); e.rethrowFromSystemServer(); @@ -1359,7 +1315,7 @@ public class DreamService extends Service implements Window.Callback { mWindow.getDecorView().addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { - private Consumer mDreamStartOverlayConsumer; + private Consumer mDreamStartOverlayConsumer; @Override public void onViewAttachedToWindow(View v) { @@ -1369,6 +1325,10 @@ public class DreamService extends Service implements Window.Callback { // Request the DreamOverlay be told to dream with dream's window // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { + if (mWindow == null) { + Slog.d(TAG, "mWindow is null"); + return; + } try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback, mDreamComponent.flattenToString(), @@ -1391,6 +1351,7 @@ public class DreamService extends Service implements Window.Callback { mActivity = null; finish(); } + if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) { mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer); } @@ -1431,10 +1392,18 @@ public class DreamService extends Service implements Window.Callback { @Nullable private static CharSequence fetchDreamLabel(Context context, - @Nullable ServiceInfo serviceInfo) { - if (serviceInfo == null) return null; + @Nullable ServiceInfo serviceInfo, + boolean isPreviewMode) { + if (serviceInfo == null) { + return null; + } final PackageManager pm = context.getPackageManager(); - return serviceInfo.loadLabel(pm); + final CharSequence dreamLabel = serviceInfo.loadLabel(pm); + if (!isPreviewMode || dreamLabel == null) { + return dreamLabel; + } + // When in preview mode, return a special label indicating the dream is in preview. + return context.getResources().getString(R.string.dream_preview_title, dreamLabel); } @Nullable @@ -1490,8 +1459,9 @@ public class DreamService extends Service implements Window.Callback { final class DreamServiceWrapper extends IDreamService.Stub { @Override public void attach(final IBinder dreamToken, final boolean canDoze, - IRemoteCallback started) { - mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started)); + final boolean isPreviewMode, IRemoteCallback started) { + mHandler.post( + () -> DreamService.this.attach(dreamToken, canDoze, isPreviewMode, started)); } @Override diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl index 7aeceb2ce5380990b22fb281c4fb446b203b8e5a..7ec75a50ed00affddde67321f58f8a202a941d96 100644 --- a/core/java/android/service/dreams/IDreamOverlay.aidl +++ b/core/java/android/service/dreams/IDreamOverlay.aidl @@ -16,8 +16,7 @@ package android.service.dreams; -import android.service.dreams.IDreamOverlayCallback; -import android.view.WindowManager.LayoutParams; +import android.service.dreams.IDreamOverlayClientCallback; /** * {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view @@ -28,17 +27,7 @@ import android.view.WindowManager.LayoutParams; */ interface IDreamOverlay { /** - * @param params The {@link LayoutParams} for the associated DreamWindow, including the window - token of the Dream Activity. - * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the - * dream. - * @param dreamComponent The component name of the dream service requesting overlay. - * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock - * and weather. + * Retrieves a client the caller can use to interact with the dream overlay. */ - void startDream(in LayoutParams params, in IDreamOverlayCallback callback, - in String dreamComponent, in boolean shouldShowComplications); - - /** Called when the dream is waking, to do any exit animations */ - void wakeUp(); + void getClient(in IDreamOverlayClientCallback callback); } diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl new file mode 100644 index 0000000000000000000000000000000000000000..78b7280ae652ec11e745616442392678c7602ffb --- /dev/null +++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.dreams; + +import android.service.dreams.IDreamOverlayCallback; +import android.view.WindowManager.LayoutParams; + +/** +* {@link IDreamOverlayClient} allows {@link DreamService} instances to act upon the dream overlay. +* +* @hide +*/ +interface IDreamOverlayClient { + /** + * @param params The {@link LayoutParams} for the associated DreamWindow, including the window + token of the Dream Activity. + * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the + * dream. + * @param dreamComponent The component name of the dream service requesting overlay. + * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock + * and weather. + */ + void startDream(in LayoutParams params, in IDreamOverlayCallback callback, + in String dreamComponent, in boolean shouldShowComplications); + + /** Called when the dream is waking, to do any exit animations */ + void wakeUp(); + + /** Called when the dream has ended. */ + void endDream(); +} diff --git a/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl b/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl new file mode 100644 index 0000000000000000000000000000000000000000..244d999c623bd605127d550f7196dace3ec99020 --- /dev/null +++ b/core/java/android/service/dreams/IDreamOverlayClientCallback.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2023, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.dreams; + +import android.service.dreams.IDreamOverlayClient; + +/** +* {@link IDreamOverlayClientCallback} allows receiving a requested {@link IDreamOverlayClient}. +* @hide +*/ +interface IDreamOverlayClientCallback { + /** + * Called with a unique {@link IDreamOverlayClient}. + */ + void onDreamOverlayClient(in IDreamOverlayClient client); +} diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl index ce04354989179e46cc49fc29ea4f7b071bc57ebd..8b5d8754647c2a56fdfe43e87c8c6a3d94d8d6eb 100644 --- a/core/java/android/service/dreams/IDreamService.aidl +++ b/core/java/android/service/dreams/IDreamService.aidl @@ -22,7 +22,7 @@ import android.os.IRemoteCallback; * @hide */ oneway interface IDreamService { - void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started); + void attach(IBinder windowToken, boolean canDoze, boolean isPreviewMode, IRemoteCallback started); void detach(); void wakeUp(); } diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 4b25c8832068f6bd78d1f0386d556e7a608b8c11..182a49758892af3b6277addf2bc0a1e56720bd4f 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -52,7 +52,7 @@ public final class Adjustment implements Parcelable { /** @hide */ @StringDef (prefix = { "KEY_" }, value = { KEY_CONTEXTUAL_ACTIONS, KEY_GROUP_KEY, KEY_IMPORTANCE, KEY_PEOPLE, KEY_SNOOZE_CRITERIA, - KEY_TEXT_REPLIES, KEY_USER_SENTIMENT + KEY_TEXT_REPLIES, KEY_USER_SENTIMENT, KEY_IMPORTANCE_PROPOSAL }) @Retention(RetentionPolicy.SOURCE) public @interface Keys {} @@ -121,6 +121,19 @@ public final class Adjustment implements Parcelable { */ public static final String KEY_IMPORTANCE = "key_importance"; + /** + * Weaker than {@link #KEY_IMPORTANCE}, this adjustment suggests an importance rather than + * mandates an importance change. + * + * A notification listener can interpet this suggestion to show the user a prompt to change + * notification importance for the notification (or type, or app) moving forward. + * + * Data type: int, one of importance values e.g. + * {@link android.app.NotificationManager#IMPORTANCE_MIN}. + * @hide + */ + public static final String KEY_IMPORTANCE_PROPOSAL = "key_importance_proposal"; + /** * Data type: float, a ranking score from 0 (lowest) to 1 (highest). * Used to rank notifications inside that fall under the same classification (i.e. alerting, diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ad2e9d5109987c46e0eca8011e7e700db4df9d24..dc4cb9f0983501c7be62aa38c3747b006bccd028 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1711,6 +1711,8 @@ public abstract class NotificationListenerService extends Service { private ShortcutInfo mShortcutInfo; private @RankingAdjustment int mRankingAdjustment; private boolean mIsBubble; + // Notification assistant importance suggestion + private int mProposedImportance; private static final int PARCEL_VERSION = 2; @@ -1748,6 +1750,7 @@ public abstract class NotificationListenerService extends Service { out.writeParcelable(mShortcutInfo, flags); out.writeInt(mRankingAdjustment); out.writeBoolean(mIsBubble); + out.writeInt(mProposedImportance); } /** @hide */ @@ -1786,6 +1789,7 @@ public abstract class NotificationListenerService extends Service { mShortcutInfo = in.readParcelable(cl, android.content.pm.ShortcutInfo.class); mRankingAdjustment = in.readInt(); mIsBubble = in.readBoolean(); + mProposedImportance = in.readInt(); } @@ -1877,6 +1881,22 @@ public abstract class NotificationListenerService extends Service { return mRankingScore; } + /** + * Returns the proposed importance provided by the {@link NotificationAssistantService}. + * + * This can be used to suggest that the user change the importance of this type of + * notification moving forward. A value of + * {@link NotificationManager#IMPORTANCE_UNSPECIFIED} means that the NAS has not recommended + * a change to the importance, and no UI should be shown to the user. See + * {@link Adjustment#KEY_IMPORTANCE_PROPOSAL}. + * + * @return the importance of the notification + * @hide + */ + public @NotificationManager.Importance int getProposedImportance() { + return mProposedImportance; + } + /** * If the system has overridden the group key, then this will be non-null, and this * key should be used to bundle notifications. @@ -2041,7 +2061,7 @@ public abstract class NotificationListenerService extends Service { boolean noisy, ArrayList smartActions, ArrayList smartReplies, boolean canBubble, boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo, - int rankingAdjustment, boolean isBubble) { + int rankingAdjustment, boolean isBubble, int proposedImportance) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -2067,6 +2087,7 @@ public abstract class NotificationListenerService extends Service { mShortcutInfo = shortcutInfo; mRankingAdjustment = rankingAdjustment; mIsBubble = isBubble; + mProposedImportance = proposedImportance; } /** @@ -2107,7 +2128,8 @@ public abstract class NotificationListenerService extends Service { other.mIsConversation, other.mShortcutInfo, other.mRankingAdjustment, - other.mIsBubble); + other.mIsBubble, + other.mProposedImportance); } /** @@ -2166,7 +2188,8 @@ public abstract class NotificationListenerService extends Service { && Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()), (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId())) && Objects.equals(mRankingAdjustment, other.mRankingAdjustment) - && Objects.equals(mIsBubble, other.mIsBubble); + && Objects.equals(mIsBubble, other.mIsBubble) + && Objects.equals(mProposedImportance, other.mProposedImportance); } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index e285b1c771221508868927814e295ed06ba7abac..29b0d444e514a19884fcf14da8b95ad88b90adb7 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -953,7 +953,7 @@ public class ZenModeConfig implements Parcelable { private static Uri safeUri(TypedXmlPullParser parser, String att) { final String val = parser.getAttributeValue(null, att); - if (TextUtils.isEmpty(val)) return null; + if (val == null) return null; return Uri.parse(val); } @@ -1008,9 +1008,8 @@ public class ZenModeConfig implements Parcelable { .allowAlarms(allowAlarms) .allowMedia(allowMedia) .allowSystem(allowSystem) - .allowConversations(allowConversations - ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom) - : ZenPolicy.PEOPLE_TYPE_NONE); + .allowConversations(allowConversations ? allowConversationsFrom + : ZenPolicy.CONVERSATION_SENDERS_NONE); if (suppressedVisualEffects == 0) { builder.showAllVisualEffects(); } else { diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java index b09d2e9e7f139b85628f7da435402fff01b7a31b..e234755b2b13df3a5736301511a28f3fd4a2b625 100644 --- a/core/java/android/service/quickaccesswallet/WalletCard.java +++ b/core/java/android/service/quickaccesswallet/WalletCard.java @@ -16,6 +16,7 @@ package android.service.quickaccesswallet; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; @@ -24,28 +25,73 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + /** * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit * card, library card, transit pass, etc. Cards are identified by a String identifier and contain a - * card image, card image content description, and a {@link PendingIntent} to be used if the user - * clicks on the card. Cards may be displayed with an icon and label, though these are optional. + * card type, card image, card image content description, and a {@link PendingIntent} to be used if + * the user clicks on the card. Cards may be displayed with an icon and label, though these are + * optional. Non-payment cards will also have a second image that will be displayed when the card is + * tapped. */ + public final class WalletCard implements Parcelable { + /** + * Unknown cards refer to cards whose types are unspecified. + * @hide + */ + public static final int CARD_TYPE_UNKNOWN = 0; + + /** + * Payment cards refer to credit cards, debit cards or any other cards in the wallet used to + * make cash-equivalent payments. + * @hide + */ + public static final int CARD_TYPE_PAYMENT = 1; + + /** + * Non-payment cards refer to any cards that are not used for cash-equivalent payment, including + * event tickets, flights, offers, loyalty cards, gift cards and transit tickets. + * @hide + */ + public static final int CARD_TYPE_NON_PAYMENT = 2; + private final String mCardId; + private final int mCardType; private final Icon mCardImage; private final CharSequence mContentDescription; private final PendingIntent mPendingIntent; private final Icon mCardIcon; private final CharSequence mCardLabel; + private final Icon mNonPaymentCardSecondaryImage; private WalletCard(Builder builder) { this.mCardId = builder.mCardId; + this.mCardType = builder.mCardType; this.mCardImage = builder.mCardImage; this.mContentDescription = builder.mContentDescription; this.mPendingIntent = builder.mPendingIntent; this.mCardIcon = builder.mCardIcon; this.mCardLabel = builder.mCardLabel; + this.mNonPaymentCardSecondaryImage = builder.mNonPaymentCardSecondaryImage; + } + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CARD_TYPE_"}, value = { + CARD_TYPE_UNKNOWN, + CARD_TYPE_PAYMENT, + CARD_TYPE_NON_PAYMENT + }) + public @interface CardType { } @Override @@ -56,29 +102,44 @@ public final class WalletCard implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mCardId); + dest.writeInt(mCardType); mCardImage.writeToParcel(dest, flags); TextUtils.writeToParcel(mContentDescription, dest, flags); PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest); - if (mCardIcon == null) { + writeIconIfNonNull(mCardIcon, dest, flags); + TextUtils.writeToParcel(mCardLabel, dest, flags); + writeIconIfNonNull(mNonPaymentCardSecondaryImage, dest, flags); + + } + + /** Utility function called by writeToParcel + */ + private void writeIconIfNonNull(Icon icon, Parcel dest, int flags) { + if (icon == null) { dest.writeByte((byte) 0); } else { dest.writeByte((byte) 1); - mCardIcon.writeToParcel(dest, flags); + icon.writeToParcel(dest, flags); } - TextUtils.writeToParcel(mCardLabel, dest, flags); } private static WalletCard readFromParcel(Parcel source) { String cardId = source.readString(); + int cardType = source.readInt(); Icon cardImage = Icon.CREATOR.createFromParcel(source); CharSequence contentDesc = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source); Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - return new Builder(cardId, cardImage, contentDesc, pendingIntent) + Icon nonPaymentCardSecondaryImage = source.readByte() == 0 ? null : + Icon.CREATOR.createFromParcel(source); + Builder builder = new Builder(cardId, cardType, cardImage, contentDesc, pendingIntent) .setCardIcon(cardIcon) - .setCardLabel(cardLabel) - .build(); + .setCardLabel(cardLabel); + + return cardType == CARD_TYPE_NON_PAYMENT + ? builder.setNonPaymentCardSecondaryImage(nonPaymentCardSecondaryImage).build() : + builder.build(); } @NonNull @@ -103,6 +164,16 @@ public final class WalletCard implements Parcelable { return mCardId; } + /** + * Returns the card type. + * @hide + */ + @NonNull + @CardType + public int getCardType() { + return mCardType; + } + /** * The visual representation of the card. If the card image Icon is a bitmap, it should have a * width of {@link GetWalletCardsRequest#getCardWidthPx()} and a height of {@link @@ -158,23 +229,37 @@ public final class WalletCard implements Parcelable { } /** - * Builder for {@link WalletCard} objects. You must to provide cardId, cardImage, + * Visual representation of the card when it is tapped. May include additional information + * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. + * @hide + */ + @Nullable + public Icon getNonPaymentCardSecondaryImage() { + return mNonPaymentCardSecondaryImage; + } + + /** + * Builder for {@link WalletCard} objects. You must provide cardId, cardImage, * contentDescription, and pendingIntent. If the card is opaque and should be shown with * elevation, set hasShadow to true. cardIcon and cardLabel are optional. */ public static final class Builder { private String mCardId; + private int mCardType; private Icon mCardImage; private CharSequence mContentDescription; private PendingIntent mPendingIntent; private Icon mCardIcon; private CharSequence mCardLabel; + private Icon mNonPaymentCardSecondaryImage; /** * @param cardId The card id must be non-null and unique within the list of * cards returned. Note: * this card ID should not contain PII (Personally * Identifiable Information, such as username or email address). + * @param cardType Integer representing the card type. The card type must be + * non-null. * @param cardImage The visual representation of the card. If the card image Icon * is a bitmap, it should have a width of {@link * GetWalletCardsRequest#getCardWidthPx()} and a height of {@link @@ -193,17 +278,32 @@ public final class WalletCard implements Parcelable { * request device unlock before sending the pending intent. It is * recommended that the pending intent be immutable (use {@link * PendingIntent#FLAG_IMMUTABLE}). + * @hide */ public Builder(@NonNull String cardId, + @NonNull @CardType int cardType, @NonNull Icon cardImage, @NonNull CharSequence contentDescription, - @NonNull PendingIntent pendingIntent) { + @NonNull PendingIntent pendingIntent + ) { mCardId = cardId; + mCardType = cardType; mCardImage = cardImage; mContentDescription = contentDescription; mPendingIntent = pendingIntent; } + /** + * Called when a card type is not provided, in which case it defaults to CARD_TYPE_UNKNOWN. + */ + public Builder(@NonNull String cardId, + @NonNull Icon cardImage, + @NonNull CharSequence contentDescription, + @NonNull PendingIntent pendingIntent) { + this(cardId, WalletCard.CARD_TYPE_UNKNOWN, cardImage, contentDescription, + pendingIntent); + } + /** * An icon may be shown alongside the card image to convey information about how the card * can be used, or if some other action must be taken before using the card. For example, an @@ -235,6 +335,20 @@ public final class WalletCard implements Parcelable { return this; } + /** + * Visual representation of the card when it is tapped. May include additional information + * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. + * @hide + */ + @NonNull + public Builder + setNonPaymentCardSecondaryImage(@Nullable Icon nonPaymentCardSecondaryImage) { + Preconditions.checkState(mCardType == CARD_TYPE_NON_PAYMENT, + "This field can only be set on non-payment cards"); + mNonPaymentCardSecondaryImage = nonPaymentCardSecondaryImage; + return this; + } + /** * Builds a new {@link WalletCard} instance. * diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index 506b3b81eb9ad0524c6d0b4d9650dd9932e94824..81c5796374af3d810c47cb2890403300aae3a7ff 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -506,7 +506,7 @@ public class TileService extends Service { * the calling package or if the calling user cannot act on behalf of the user from the * {@code context}. *
  • {@link IllegalArgumentException} if the user of the {@code context} is not the - * current user.
  • + * current user. Only thrown for apps targeting {@link Build.VERSION_CODES#TIRAMISU} * */ public static final void requestListeningState(Context context, ComponentName component) { diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java index 3a148dffe6d6996c5f7f631d6ea238483452bd52..b13a069116af088d3a790a1af43123b6dd4c5050 100644 --- a/core/java/android/service/smartspace/SmartspaceService.java +++ b/core/java/android/service/smartspace/SmartspaceService.java @@ -302,7 +302,7 @@ public abstract class SmartspaceService extends Service { Slog.e(TAG, "Callback is null, likely the binder has died."); return false; } - return mCallback.equals(callback); + return mCallback.asBinder().equals(callback.asBinder()); } @Override diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index 1a00acf475e3db905a9af91ef03c39c7f40ecea0..15a8502a2e7ff5999dcd4372b7dc57645fa54165 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -32,6 +32,8 @@ interface IWallpaperEngine { oneway void setDisplayPadding(in Rect padding); @UnsupportedAppUsage oneway void setVisibility(boolean visible); + oneway void onScreenTurningOn(); + oneway void onScreenTurnedOn(); oneway void setInAmbientMode(boolean inAmbientDisplay, long animationDuration); @UnsupportedAppUsage oneway void dispatchPointer(in MotionEvent event); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 2d1a41e92a99a8d0ebf04badd02d41f5b6459aac..8f1fc1b9348e7fdbab4b4bce77adf8f0336762e2 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -61,6 +61,7 @@ import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -96,6 +97,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.window.ClientWindowFrames; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; @@ -104,9 +106,10 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -165,12 +168,15 @@ public abstract class WallpaperService extends Service { private static final int MSG_ZOOM = 10100; private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_REPORT_SHOWN = 10150; + private static final int MSG_UPDATE_SCREEN_TURNING_ON = 10170; private static final int MSG_UPDATE_DIMMING = 10200; - private static final List PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, - Float.NEGATIVE_INFINITY); + /** limit calls to {@link Engine#onComputeColors} to at most once per second */ private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; + /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */ + private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000; + private static final boolean ENABLE_WALLPAPER_DIMMING = SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); @@ -179,6 +185,9 @@ public abstract class WallpaperService extends Service { private final ArrayList mActiveEngines = new ArrayList(); + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + static final class WallpaperCommand { String action; int x; @@ -197,14 +206,6 @@ public abstract class WallpaperService extends Service { */ public class Engine { IWallpaperEngineWrapper mIWallpaperEngine; - final ArraySet mLocalColorAreas = new ArraySet<>(4); - final ArraySet mLocalColorsToAdd = new ArraySet<>(4); - - // 2D matrix [x][y] to represent a page of a portion of a window - EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; - Bitmap mLastScreenshot; - int mLastWindowPage = -1; - private boolean mResetWindowPages; // Copies from mIWallpaperEngine. HandlerCaller mCaller; @@ -213,6 +214,16 @@ public abstract class WallpaperService extends Service { boolean mInitializing = true; boolean mVisible; + /** + * Whether the screen is turning on. + * After the display is powered on, brightness is initially off. It is turned on only after + * all windows have been drawn, and sysui notifies that it's ready (See + * {@link com.android.internal.policy.IKeyguardDrawnCallback}). + * As some wallpapers use visibility as a signal to start animations, this makes sure + * {@link Engine#onVisibilityChanged} is invoked only when the display is both on and + * visible (with brightness on). + */ + private boolean mIsScreenTurningOn; boolean mReportedVisible; boolean mDestroyed; // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false @@ -266,21 +277,44 @@ public abstract class WallpaperService extends Service { final Object mLock = new Object(); boolean mOffsetMessageEnqueued; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - float mPendingXOffset; - float mPendingYOffset; - float mPendingXOffsetStep; - float mPendingYOffsetStep; + @GuardedBy("mLock") + private float mPendingXOffset; + @GuardedBy("mLock") + private float mPendingYOffset; + @GuardedBy("mLock") + private float mPendingXOffsetStep; + @GuardedBy("mLock") + private float mPendingYOffsetStep; + + /** + * local color extraction related fields. When a user calls `addLocalColorAreas` + */ + @GuardedBy("mLock") + private final ArraySet mLocalColorAreas = new ArraySet<>(4); + + @GuardedBy("mLock") + private final ArraySet mLocalColorsToAdd = new ArraySet<>(4); + private long mLastProcessLocalColorsTimestamp; + private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); + private int mPixelCopyCount = 0; + // 2D matrix [x][y] to represent a page of a portion of a window + @GuardedBy("mLock") + private EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; + private Bitmap mLastScreenshot; + private boolean mResetWindowPages; + boolean mPendingSync; MotionEvent mPendingMove; boolean mIsInAmbientMode; - // Needed for throttling onComputeColors. + // used to throttle onComputeColors private long mLastColorInvalidation; private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; + private final Supplier mClockFunction; private final Handler mHandler; - private Display mDisplay; private Context mDisplayContext; private int mDisplayState; @@ -820,7 +854,7 @@ public abstract class WallpaperService extends Service { + "was not established."); } mResetWindowPages = true; - processLocalColors(mPendingXOffset, mPendingXOffsetStep); + processLocalColors(); } catch (RemoteException e) { Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } @@ -965,6 +999,7 @@ public abstract class WallpaperService extends Service { out.print(" mDestroyed="); out.println(mDestroyed); out.print(prefix); out.print("mVisible="); out.print(mVisible); out.print(" mReportedVisible="); out.println(mReportedVisible); + out.print(" mIsScreenTurningOn="); out.println(mIsScreenTurningOn); out.print(prefix); out.print("mDisplay="); out.println(mDisplay); out.print(prefix); out.print("mCreated="); out.print(mCreated); out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); @@ -1356,10 +1391,9 @@ public abstract class WallpaperService extends Service { mIsCreating = false; mSurfaceCreated = true; if (redrawNeeded) { - resetWindowPages(); mSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE); - processLocalColors(mPendingXOffset, mPendingXOffsetStep); + processLocalColors(); } reposition(); reportEngineShown(shouldWaitForEngineShown()); @@ -1425,7 +1459,7 @@ public abstract class WallpaperService extends Service { com.android.internal.R.dimen.config_wallpaperDimAmount); mWallpaperDimAmount = mDefaultDimAmount; mPreviousWallpaperDimAmount = mWallpaperDimAmount; - mDisplayState = mDisplay.getState(); + mDisplayState = mDisplay.getCommittedState(); mDisplayInstallOrientation = mDisplay.getInstallOrientation(); if (DEBUG) Log.v(TAG, "onCreate(): " + this); @@ -1500,11 +1534,18 @@ public abstract class WallpaperService extends Service { } } + void onScreenTurningOnChanged(boolean isScreenTurningOn) { + if (!mDestroyed) { + mIsScreenTurningOn = isScreenTurningOn; + reportVisibility(); + } + } + void doVisibilityChanged(boolean visible) { if (!mDestroyed) { mVisible = visible; reportVisibility(); - if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); + if (mReportedVisible) processLocalColors(); } else { AnimationHandler.requestAnimatorsEnabled(visible, this); } @@ -1516,8 +1557,10 @@ public abstract class WallpaperService extends Service { return; } if (!mDestroyed) { - mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); - boolean visible = mVisible && mDisplayState != Display.STATE_OFF; + mDisplayState = + mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getCommittedState(); + boolean displayVisible = Display.isOnState(mDisplayState) && !mIsScreenTurningOn; + boolean visible = mVisible && displayVisible; if (mReportedVisible != visible) { mReportedVisible = visible; if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible @@ -1588,48 +1631,83 @@ public abstract class WallpaperService extends Service { } // setup local color extraction data - processLocalColors(xOffset, xOffsetStep); + processLocalColors(); } - private void processLocalColors(float xOffset, float xOffsetStep) { - // implemented by the wallpaper - if (supportsLocalColorExtraction()) return; - if (DEBUG) { - Log.d(TAG, "processLocalColors " + xOffset + " of step " - + xOffsetStep); + /** + * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of + * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. + */ + private void processLocalColors() { + if (mProcessLocalColorsPending.compareAndSet(false, true)) { + final long now = mClockFunction.get(); + final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; + final long timeToWait = Math.max(0, + PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess); + + mHandler.postDelayed(() -> { + mLastProcessLocalColorsTimestamp = now + timeToWait; + mProcessLocalColorsPending.set(false); + processLocalColorsInternal(); + }, timeToWait); } - //below is the default implementation - if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN - || !mSurfaceHolder.getSurface().isValid()) return; - int xCurrentPage; + } + + /** + * Default implementation of the local color extraction. + * This will take a screenshot of the whole wallpaper on the main thread. + * Then, in a background thread, for each launcher page, for each area that needs color + * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap} + * to extract the colors. Every time a launcher page has been processed, call + * {@link #notifyLocalColorsChanged} with the color and areas of this page. + */ + private void processLocalColorsInternal() { + if (supportsLocalColorExtraction()) return; + float xOffset; + float xOffsetStep; + float wallpaperDimAmount; + int xPage; int xPages; - if (!validStep(xOffsetStep)) { + Set areas; + EngineWindowPage current; + + synchronized (mLock) { + xOffset = mPendingXOffset; + xOffsetStep = mPendingXOffsetStep; + wallpaperDimAmount = mWallpaperDimAmount; + if (DEBUG) { - Log.w(TAG, "invalid offset step " + xOffsetStep); + Log.d(TAG, "processLocalColors " + xOffset + " of step " + + xOffsetStep); } - xOffset = 0; - xOffsetStep = 1; - xCurrentPage = 0; - xPages = 1; - } else { - xPages = Math.round(1 / xOffsetStep) + 1; - xOffsetStep = (float) 1 / (float) xPages; - float shrink = (float) (xPages - 1) / (float) xPages; - xOffset *= shrink; - xCurrentPage = Math.round(xOffset / xOffsetStep); - } - if (DEBUG) { - Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); - Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); - } + if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN + || !mSurfaceHolder.getSurface().isValid()) return; + int xCurrentPage; + if (!validStep(xOffsetStep)) { + if (DEBUG) { + Log.w(TAG, "invalid offset step " + xOffsetStep); + } + xOffset = 0; + xOffsetStep = 1; + xCurrentPage = 0; + xPages = 1; + } else { + xPages = Math.round(1 / xOffsetStep) + 1; + xOffsetStep = (float) 1 / (float) xPages; + float shrink = (float) (xPages - 1) / (float) xPages; + xOffset *= shrink; + xCurrentPage = Math.round(xOffset / xOffsetStep); + } + if (DEBUG) { + Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); + Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); + } + + float finalXOffsetStep = xOffsetStep; + float finalXOffset = xOffset; - float finalXOffsetStep = xOffsetStep; - float finalXOffset = xOffset; - mHandler.post(() -> { - Trace.beginSection("WallpaperService#processLocalColors"); resetWindowPages(); - int xPage = xCurrentPage; - EngineWindowPage current; + xPage = xCurrentPage; if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { mWindowPages = new EngineWindowPage[xPages]; initWindowPages(mWindowPages, finalXOffsetStep); @@ -1656,11 +1734,12 @@ public abstract class WallpaperService extends Service { xPage = mWindowPages.length - 1; } current = mWindowPages[xPage]; - updatePage(current, xPage, xPages, finalXOffsetStep); - Trace.endSection(); - }); + areas = new HashSet<>(current.getAreas()); + } + updatePage(current, areas, xPage, xPages, wallpaperDimAmount); } + @GuardedBy("mLock") private void initWindowPages(EngineWindowPage[] windowPages, float step) { for (int i = 0; i < windowPages.length; i++) { windowPages[i] = new EngineWindowPage(); @@ -1677,16 +1756,16 @@ public abstract class WallpaperService extends Service { } } - void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, - float xOffsetStep) { + void updatePage(EngineWindowPage currentPage, Set areas, int pageIndx, int numPages, + float wallpaperDimAmount) { + // in case the clock is zero, we start with negative time long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long lapsed = current - currentPage.getLastUpdateTime(); // Always update the page when the last update time is <= 0 // This is important especially when the device first boots - if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) { - return; - } + if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return; + Surface surface = mSurfaceHolder.getSurface(); if (!surface.isValid()) return; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; @@ -1699,43 +1778,59 @@ public abstract class WallpaperService extends Service { Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); return; } + final String pixelCopySectionName = "WallpaperService#pixelCopy"; + final int pixelCopyCount = mPixelCopyCount++; + Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount); Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Bitmap finalScreenShot = screenShot; - Trace.beginSection("WallpaperService#pixelCopy"); - PixelCopy.request(surface, screenShot, (res) -> { - Trace.endSection(); - if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); - if (res != PixelCopy.SUCCESS) { - Bitmap lastBitmap = currentPage.getBitmap(); - // assign the last bitmap taken for now - currentPage.setBitmap(mLastScreenshot); - Bitmap lastScreenshot = mLastScreenshot; - if (lastScreenshot != null && !lastScreenshot.isRecycled() - && !Objects.equals(lastBitmap, lastScreenshot)) { - updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); + try { + // TODO(b/274427458) check if this can be done in the background. + PixelCopy.request(surface, screenShot, (res) -> { + Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount); + if (DEBUG) { + Log.d(TAG, "result of pixel copy is: " + + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE")); } - } else { - mLastScreenshot = finalScreenShot; - // going to hold this lock for a while - currentPage.setBitmap(finalScreenShot); - currentPage.setLastUpdateTime(current); - updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); - } - }, mHandler); - + if (res != PixelCopy.SUCCESS) { + Bitmap lastBitmap = currentPage.getBitmap(); + // assign the last bitmap taken for now + currentPage.setBitmap(mLastScreenshot); + Bitmap lastScreenshot = mLastScreenshot; + if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) { + updatePageColors( + currentPage, areas, pageIndx, numPages, wallpaperDimAmount); + } + } else { + mLastScreenshot = finalScreenShot; + currentPage.setBitmap(finalScreenShot); + currentPage.setLastUpdateTime(current); + updatePageColors( + currentPage, areas, pageIndx, numPages, wallpaperDimAmount); + } + }, mBackgroundHandler); + } catch (IllegalArgumentException e) { + // this can potentially happen if the surface is invalidated right between the + // surface.isValid() check and the PixelCopy operation. + // in this case, stop: we'll compute colors on the next processLocalColors call. + Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy"); + } } // locked by the passed page - private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, - float xOffsetStep) { + private void updatePageColors(EngineWindowPage page, Set areas, + int pageIndx, int numPages, float wallpaperDimAmount) { if (page.getBitmap() == null) return; + if (!mBackgroundHandler.getLooper().isCurrentThread()) { + throw new IllegalStateException( + "ProcessLocalColors should be called from the background thread"); + } Trace.beginSection("WallpaperService#updatePageColors"); if (DEBUG) { Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " + page.getAreas().size() + " and bitmap size of " + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); } - for (RectF area: page.getAreas()) { + for (RectF area: areas) { if (area == null) continue; RectF subArea = generateSubRect(area, pageIndx, numPages); Bitmap b = page.getBitmap(); @@ -1745,12 +1840,12 @@ public abstract class WallpaperService extends Service { int height = Math.round(b.getHeight() * subArea.height()); Bitmap target; try { - target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); + target = Bitmap.createBitmap(b, x, y, width, height); } catch (Exception e) { Log.e(TAG, "Error creating page local color bitmap", e); continue; } - WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount); + WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount); target.recycle(); WallpaperColors currentColor = page.getColors(area); @@ -1767,12 +1862,14 @@ public abstract class WallpaperService extends Service { + " local color callback for area" + area + " for page " + pageIndx + " of " + numPages); } - try { - mConnection.onLocalWallpaperColorsChanged(area, color, - mDisplayContext.getDisplayId()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); - } + mHandler.post(() -> { + try { + mConnection.onLocalWallpaperColorsChanged(area, color, + mDisplayContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); + } + }); } } Trace.endSection(); @@ -1798,16 +1895,17 @@ public abstract class WallpaperService extends Service { return new RectF(left, in.top, right, in.bottom); } + @GuardedBy("mLock") private void resetWindowPages() { if (supportsLocalColorExtraction()) return; if (!mResetWindowPages) return; mResetWindowPages = false; - mLastWindowPage = -1; for (int i = 0; i < mWindowPages.length; i++) { mWindowPages[i].setLastUpdateTime(0L); } } + @GuardedBy("mLock") private int getRectFPage(RectF area, float step) { if (!isValid(area)) return 0; if (!validStep(step)) return 0; @@ -1828,12 +1926,12 @@ public abstract class WallpaperService extends Service { if (DEBUG) { Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); } - mHandler.post(() -> { - mLocalColorsToAdd.addAll(regions); - processLocalColors(mPendingXOffset, mPendingYOffset); + mBackgroundHandler.post(() -> { + synchronized (mLock) { + mLocalColorsToAdd.addAll(regions); + } + processLocalColors(); }); - - } /** @@ -1843,16 +1941,18 @@ public abstract class WallpaperService extends Service { */ public void removeLocalColorsAreas(@NonNull List regions) { if (supportsLocalColorExtraction()) return; - mHandler.post(() -> { - float step = mPendingXOffsetStep; - mLocalColorsToAdd.removeAll(regions); - mLocalColorAreas.removeAll(regions); - if (!validStep(step)) { - return; - } - for (int i = 0; i < mWindowPages.length; i++) { - for (int j = 0; j < regions.size(); j++) { - mWindowPages[i].removeArea(regions.get(j)); + mBackgroundHandler.post(() -> { + synchronized (mLock) { + float step = mPendingXOffsetStep; + mLocalColorsToAdd.removeAll(regions); + mLocalColorAreas.removeAll(regions); + if (!validStep(step)) { + return; + } + for (int i = 0; i < mWindowPages.length; i++) { + for (int j = 0; j < regions.size(); j++) { + mWindowPages[i].removeArea(regions.get(j)); + } } } }); @@ -1870,7 +1970,7 @@ public abstract class WallpaperService extends Service { } private boolean validStep(float step) { - return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.; + return !Float.isNaN(step) && step > 0f && step <= 1f; } void doCommand(WallpaperCommand cmd) { @@ -2334,6 +2434,20 @@ public abstract class WallpaperService extends Service { } } + public void updateScreenTurningOn(boolean isScreenTurningOn) { + Message msg = mCaller.obtainMessageBO(MSG_UPDATE_SCREEN_TURNING_ON, isScreenTurningOn, + null); + mCaller.sendMessage(msg); + } + + public void onScreenTurningOn() throws RemoteException { + updateScreenTurningOn(true); + } + + public void onScreenTurnedOn() throws RemoteException { + updateScreenTurningOn(false); + } + @Override public void executeMessage(Message message) { if (mDetached.get()) { @@ -2384,6 +2498,13 @@ public abstract class WallpaperService extends Service { + ": " + message.arg1); mEngine.doVisibilityChanged(message.arg1 != 0); break; + case MSG_UPDATE_SCREEN_TURNING_ON: + if (DEBUG) { + Log.v(TAG, + message.arg1 != 0 ? "Screen turning on" : "Screen turned on"); + } + mEngine.onScreenTurningOnChanged(/* isScreenTurningOn= */ message.arg1 != 0); + break; case MSG_WALLPAPER_OFFSETS: { mEngine.doOffsetsChanged(true); } break; @@ -2474,6 +2595,9 @@ public abstract class WallpaperService extends Service { @Override public void onCreate() { Trace.beginSection("WPMS.onCreate"); + mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); super.onCreate(); Trace.endSection(); } @@ -2486,6 +2610,7 @@ public abstract class WallpaperService extends Service { mActiveEngines.get(i).detach(); } mActiveEngines.clear(); + mBackgroundThread.quitSafely(); Trace.endSection(); } diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java index a1d6cc8e283a15934671d28c51d89c125b747882..6da0b63dbc1f3124721eb73990111eadbf109ab7 100644 --- a/core/java/android/text/TextShaper.java +++ b/core/java/android/text/TextShaper.java @@ -173,7 +173,7 @@ public class TextShaper { private TextShaper() {} /** - * An consumer interface for accepting text shape result. + * A consumer interface for accepting text shape result. */ public interface GlyphsConsumer { /** diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java index b7873b73cb28ace5a5689b08d23b12ed139ee6ee..61f29a40ff501c49bb2948c2df4804c2f316a01d 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -139,4 +139,9 @@ public class SparseSetArray { public T valueAt(int intIndex, int valueIndex) { return mData.valueAt(intIndex).valueAt(valueIndex); } + + /** @return The set of values for key at position {@code intIndex}. */ + public ArraySet valuesAt(int intIndex) { + return mData.valueAt(intIndex); + } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index eb467e0dcc38b138cd95f9c0b7d5db2de7aa86cb..992a5861e07707dc06a6fb1a550dbabd90005328 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -74,6 +74,11 @@ public class ApkSignatureSchemeV2Verifier { private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; + /** + * The maximum number of signers supported by the v2 APK signature scheme. + */ + private static final int MAX_V2_SIGNERS = 10; + /** * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature. * @@ -183,6 +188,11 @@ public class ApkSignatureSchemeV2Verifier { } while (signers.hasRemaining()) { signerCount++; + if (signerCount > MAX_V2_SIGNERS) { + throw new SecurityException( + "APK Signature Scheme v2 only supports a maximum of " + MAX_V2_SIGNERS + + " signers"); + } try { ByteBuffer signer = getLengthPrefixedSlice(signers); X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory); diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java index 45254908c5c967400cfeb77b3f3f873f72ae9571..a6aca330d323ebf85f74d6aaa59ccc1894fbb91f 100644 --- a/core/java/android/util/jar/StrictJarVerifier.java +++ b/core/java/android/util/jar/StrictJarVerifier.java @@ -78,6 +78,11 @@ class StrictJarVerifier { "SHA1", }; + /** + * The maximum number of signers supported by the JAR signature scheme. + */ + private static final int MAX_JAR_SIGNERS = 10; + private final String jarName; private final StrictJarManifest manifest; private final HashMap metaEntries; @@ -293,10 +298,16 @@ class StrictJarVerifier { return false; } + int signerCount = 0; Iterator it = metaEntries.keySet().iterator(); while (it.hasNext()) { String key = it.next(); if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) { + if (++signerCount > MAX_JAR_SIGNERS) { + throw new SecurityException( + "APK Signature Scheme v1 only supports a maximum of " + MAX_JAR_SIGNERS + + " signers"); + } verifyCertificate(key); it.remove(); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 52d222b19b6ac1f6a0b72315041ca73cf3ea891d..f85f9067e347676a3137207187d927cff27ff2a4 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1599,6 +1599,21 @@ public final class Display { } } + /** + * Returns the committed state of the display. + * + * @return The latest committed display state, such as {@link #STATE_ON}. The display state + * {@link Display#getState()} is set as committed only after power state changes finish. + * + * @hide + */ + public int getCommittedState() { + synchronized (mLock) { + updateDisplayInfoLocked(); + return mIsValid ? mDisplayInfo.committedState : STATE_UNKNOWN; + } + } + /** * Returns true if the specified UID has access to this display. * @hide diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 12ce8ee5e0ad0e81f6acc3bb53eca27531f568e5..f65a69a8e2bc182870339de8991ce1bb23ff7db5 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -252,6 +252,12 @@ public final class DisplayInfo implements Parcelable { */ public int state; + /** + * The current committed state of the display. For example, this becomes + * {@link android.view.Display#STATE_ON} only after the power state ON is fully committed. + */ + public int committedState; + /** * The UID of the application that owns this display, or zero if it is owned by the system. *

    @@ -380,6 +386,7 @@ public final class DisplayInfo implements Parcelable { && appVsyncOffsetNanos == other.appVsyncOffsetNanos && presentationDeadlineNanos == other.presentationDeadlineNanos && state == other.state + && committedState == other.committedState && ownerUid == other.ownerUid && Objects.equals(ownerPackageName, other.ownerPackageName) && removeMode == other.removeMode @@ -431,6 +438,7 @@ public final class DisplayInfo implements Parcelable { appVsyncOffsetNanos = other.appVsyncOffsetNanos; presentationDeadlineNanos = other.presentationDeadlineNanos; state = other.state; + committedState = other.committedState; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; removeMode = other.removeMode; @@ -482,6 +490,7 @@ public final class DisplayInfo implements Parcelable { appVsyncOffsetNanos = source.readLong(); presentationDeadlineNanos = source.readLong(); state = source.readInt(); + committedState = source.readInt(); ownerUid = source.readInt(); ownerPackageName = source.readString8(); uniqueId = source.readString8(); @@ -538,6 +547,7 @@ public final class DisplayInfo implements Parcelable { dest.writeLong(appVsyncOffsetNanos); dest.writeLong(presentationDeadlineNanos); dest.writeInt(state); + dest.writeInt(committedState); dest.writeInt(ownerUid); dest.writeString8(ownerPackageName); dest.writeString8(uniqueId); @@ -761,6 +771,8 @@ public final class DisplayInfo implements Parcelable { sb.append(rotation); sb.append(", state "); sb.append(Display.stateToString(state)); + sb.append(", committedState "); + sb.append(Display.stateToString(committedState)); if (Process.myUid() != Process.SYSTEM_UID) { sb.append("}"); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 06e96ef58cdaaa17e3a79e21504edc7a89f689ec..483c39d98ae16b20cf1ce7cbf9b8ab0fb90bf7ea 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,6 +17,7 @@ package android.view; import static android.content.res.Resources.ID_NULL; +import static android.os.Trace.TRACE_TAG_APP; import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS; @@ -984,6 +985,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sAcceptZeroSizeDragShadow; + /** + * When true, measure and layout passes of all the newly attached views will be logged with + * {@link Trace}, so we can better debug jank due to complex view hierarchies. + */ + private static boolean sTraceLayoutSteps; + + /** + * When not null, emits a {@link Trace} instant event and the stacktrace every time a relayout + * of a class having this name happens. + */ + private static String sTraceRequestLayoutClass; + + /** Used to avoid computing the full strings each time when layout tracing is enabled. */ + @Nullable + private ViewTraversalTracingStrings mTracingStrings; + /** * Prior to R, {@link #dispatchApplyWindowInsets} had an issue: *

    The modified insets changed by {@link #onApplyWindowInsets} were passed to the @@ -3532,6 +3549,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE * 1 PFLAG4_DRAG_A11Y_STARTED * 1 PFLAG4_AUTO_HANDWRITING_INITIATION_ENABLED + * 1 PFLAG4_TRAVERSAL_TRACING_ENABLED + * 1 PFLAG4_RELAYOUT_TRACING_ENABLED * |-------|-------|-------|-------| */ @@ -3612,6 +3631,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Indicates that the view enables auto handwriting initiation. */ private static final int PFLAG4_AUTO_HANDWRITING_ENABLED = 0x000010000; + + /** + * When set, measure and layout passes of this view will be logged with {@link Trace}, so we + * can better debug jank due to complex view hierarchies. + */ + private static final int PFLAG4_TRAVERSAL_TRACING_ENABLED = 0x000040000; + + /** + * When set, emits a {@link Trace} instant event and stacktrace every time a requestLayout of + * this class happens. + */ + private static final int PFLAG4_RELAYOUT_TRACING_ENABLED = 0x000080000; + /* End of masks for mPrivateFlags4 */ /** @hide */ @@ -6537,6 +6569,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, out.append(mRight); out.append(','); out.append(mBottom); + appendId(out); + if (mAutofillId != null) { + out.append(" aid="); out.append(mAutofillId); + } + out.append("}"); + return out.toString(); + } + + void appendId(StringBuilder out) { final int id = getId(); if (id != NO_ID) { out.append(" #"); @@ -6568,11 +6609,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - if (mAutofillId != null) { - out.append(" aid="); out.append(mAutofillId); - } - out.append("}"); - return out.toString(); } /** @@ -8738,7 +8774,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @UnsupportedAppUsage - public void getBoundsOnScreen(Rect outRect, boolean clipToParent) { + @TestApi + public void getBoundsOnScreen(@NonNull Rect outRect, boolean clipToParent) { if (mAttachInfo == null) { return; } @@ -8746,6 +8783,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, getBoundsToScreenInternal(position, clipToParent); outRect.set(Math.round(position.left), Math.round(position.top), Math.round(position.right), Math.round(position.bottom)); + // If "Sandboxing View Bounds APIs" override is enabled, applyViewBoundsSandboxingIfNeeded + // will sandbox outRect within window bounds. + mAttachInfo.mViewRootImpl.applyViewBoundsSandboxingIfNeeded(outRect); } /** @@ -15558,7 +15598,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @UnsupportedAppUsage - public void getWindowDisplayFrame(Rect outRect) { + @TestApi + public void getWindowDisplayFrame(@NonNull Rect outRect) { if (mAttachInfo != null) { mAttachInfo.mViewRootImpl.getDisplayFrame(outRect); return; @@ -20775,6 +20816,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (isFocused()) { notifyFocusChangeToImeFocusController(true /* hasFocus */); } + + if (sTraceLayoutSteps) { + setTraversalTracingEnabled(true); + } + if (sTraceRequestLayoutClass != null + && sTraceRequestLayoutClass.equals(getClass().getSimpleName())) { + setRelayoutTracingEnabled(true); + } } /** @@ -23674,6 +23723,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical(); } + /** + * Enable measure/layout debugging on traces. + * + * @see Trace + * @hide + */ + public static void setTraceLayoutSteps(boolean traceLayoutSteps) { + sTraceLayoutSteps = traceLayoutSteps; + } + + /** + * Enable request layout tracing classes with {@code s} simple name. + *

    + * When set, a {@link Trace} instant event and a log with the stacktrace is emitted every + * time a requestLayout of a class matching {@code s} name happens. + * This applies only to views attached from this point onwards. + * + * @see Trace#instant(long, String) + * @hide + */ + public static void setTracedRequestLayoutClassClass(String s) { + sTraceRequestLayoutClass = s; + } + private boolean setOpticalFrame(int left, int top, int right, int bottom) { Insets parentInsets = mParent instanceof View ? ((View) mParent).getOpticalInsets() : Insets.NONE; @@ -23708,7 +23781,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @SuppressWarnings({"unchecked"}) public void layout(int l, int t, int r, int b) { if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { + if (isTraversalTracingEnabled()) { + Trace.beginSection(mTracingStrings.onMeasureBeforeLayout); + } onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); + if (isTraversalTracingEnabled()) { + Trace.endSection(); + } mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } @@ -23721,7 +23800,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { + if (isTraversalTracingEnabled()) { + Trace.beginSection(mTracingStrings.onLayout); + } onLayout(changed, l, t, r, b); + if (isTraversalTracingEnabled()) { + Trace.endSection(); + } if (shouldDrawRoundScrollbar()) { if(mRoundScrollbarRenderer == null) { @@ -25714,6 +25799,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (info != null) { outLocation[0] += info.mWindowLeft; outLocation[1] += info.mWindowTop; + // If OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS override is enabled, + // applyViewLocationSandboxingIfNeeded sandboxes outLocation within window bounds. + info.mViewRootImpl.applyViewLocationSandboxingIfNeeded(outLocation); } } @@ -26278,6 +26366,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (viewRoot != null && viewRoot.isInLayout()); } + /** To be used only for debugging purposes. */ + private void printStackStrace(String name) { + Log.d(VIEW_LOG_TAG, "---- ST:" + name); + + StringBuilder sb = new StringBuilder(); + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + int startIndex = 1; + int endIndex = Math.min(stackTraceElements.length, startIndex + 20); // max 20 entries. + for (int i = startIndex; i < endIndex; i++) { + StackTraceElement s = stackTraceElements[i]; + sb.append(s.getMethodName()) + .append("(") + .append(s.getFileName()) + .append(":") + .append(s.getLineNumber()) + .append(") <- "); + } + Log.d(VIEW_LOG_TAG, name + ": " + sb); + } /** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view @@ -26291,6 +26398,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @CallSuper public void requestLayout() { + if (isRelayoutTracingEnabled()) { + Trace.instantForTrack(TRACE_TAG_APP, "requestLayoutTracing", + mTracingStrings.classSimpleName); + printStackStrace(mTracingStrings.requestLayoutStacktracePrefix); + } + if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { @@ -26384,8 +26497,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { + if (isTraversalTracingEnabled()) { + Trace.beginSection(mTracingStrings.onMeasure); + } // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); + if (isTraversalTracingEnabled()) { + Trace.endSection(); + } mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); @@ -31555,6 +31674,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback, == PFLAG4_AUTO_HANDWRITING_ENABLED; } + private void setTraversalTracingEnabled(boolean enabled) { + if (enabled) { + if (mTracingStrings == null) { + mTracingStrings = new ViewTraversalTracingStrings(this); + } + mPrivateFlags4 |= PFLAG4_TRAVERSAL_TRACING_ENABLED; + } else { + mPrivateFlags4 &= ~PFLAG4_TRAVERSAL_TRACING_ENABLED; + } + } + + private boolean isTraversalTracingEnabled() { + return (mPrivateFlags4 & PFLAG4_TRAVERSAL_TRACING_ENABLED) + == PFLAG4_TRAVERSAL_TRACING_ENABLED; + } + + private void setRelayoutTracingEnabled(boolean enabled) { + if (enabled) { + if (mTracingStrings == null) { + mTracingStrings = new ViewTraversalTracingStrings(this); + } + mPrivateFlags4 |= PFLAG4_RELAYOUT_TRACING_ENABLED; + } else { + mPrivateFlags4 &= ~PFLAG4_RELAYOUT_TRACING_ENABLED; + } + } + + private boolean isRelayoutTracingEnabled() { + return (mPrivateFlags4 & PFLAG4_RELAYOUT_TRACING_ENABLED) + == PFLAG4_RELAYOUT_TRACING_ENABLED; + } + /** * Collects a {@link ViewTranslationRequest} which represents the content to be translated in * the view. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5f04d5846977194d7b45a7df3fc1a83d79bb4373..6feb899745dcaeaa70812d97efc55a631de4a8e0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,7 @@ package android.view; +import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS; import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED; import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND; import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID; @@ -73,15 +74,16 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CO import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; @@ -94,12 +96,14 @@ import android.animation.LayoutTransition; import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Size; import android.annotation.UiContext; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ICompatCameraControlCallback; import android.app.ResourcesManager; import android.app.WindowConfiguration; +import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.ClipDescription; @@ -711,6 +715,7 @@ public final class ViewRootImpl implements ViewParent, // These are accessed by multiple threads. final Rect mWinFrame; // frame given by window manager. + private final Rect mLastLayoutFrame; Rect mOverrideInsetsFrame; final Rect mPendingBackDropFrame = new Rect(); @@ -870,6 +875,15 @@ public final class ViewRootImpl implements ViewParent, private boolean mRelayoutRequested; + /** + * Whether sandboxing of {@link android.view.View#getBoundsOnScreen}, + * {@link android.view.View#getLocationOnScreen(int[])}, + * {@link android.view.View#getWindowDisplayFrame} and + * {@link android.view.View#getWindowVisibleDisplayFrame} + * within Activity bounds is enabled for the current application. + */ + private final boolean mViewBoundsSandboxingEnabled; + private int mLastTransformHint = Integer.MIN_VALUE; /** @@ -931,6 +945,7 @@ public final class ViewRootImpl implements ViewParent, mHeight = -1; mDirty = new Rect(); mWinFrame = new Rect(); + mLastLayoutFrame = new Rect(); mWindow = new W(this); mLeashToken = new Binder(); mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -956,6 +971,8 @@ public final class ViewRootImpl implements ViewParent, mHandwritingInitiator = new HandwritingInitiator(mViewConfiguration, mContext.getSystemService(InputMethodManager.class)); + mViewBoundsSandboxingEnabled = getViewBoundsSandboxingEnabled(); + String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); if (processorOverrideName.isEmpty()) { @@ -1112,6 +1129,8 @@ public final class ViewRootImpl implements ViewParent, // Update the last resource config in case the resource configuration was changed while // activity relaunched. updateLastConfigurationFromResources(getConfiguration()); + // Make sure to report the completion of draw for relaunch with preserved window. + reportNextDraw("rebuilt"); } private Configuration getConfiguration() { @@ -1273,7 +1292,7 @@ public final class ViewRootImpl implements ViewParent, mTmpFrames.attachedFrame = attachedFrame; mTmpFrames.compatScale = compatScale[0]; mInvCompatScale = 1f / compatScale[0]; - } catch (RemoteException e) { + } catch (RemoteException | RuntimeException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; @@ -1301,7 +1320,7 @@ public final class ViewRootImpl implements ViewParent, UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH, mInsetsController.getRequestedVisibilities(), 1f /* compactScale */, mTmpFrames); - setFrame(mTmpFrames.frame); + setFrame(mTmpFrames.frame, true /* withinRelayout */); registerBackCallbackOnWindow(); if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // For apps requesting legacy back behavior, we add a compat callback that @@ -1389,6 +1408,8 @@ public final class ViewRootImpl implements ViewParent, listener, listener.data, mHandler, true /*waitForPresentTime*/); mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); } + // Update unbuffered request when set the root view. + mUnbufferedInputSource = mView.mUnbufferedInputSource; } view.assignParent(this); @@ -1825,7 +1846,7 @@ public final class ViewRootImpl implements ViewParent, onMovedToDisplay(displayId, mLastConfigurationFromResources); } - setFrame(frame); + setFrame(frame, false /* withinRelayout */); mTmpFrames.displayFrame.set(displayFrame); if (mTmpFrames.attachedFrame != null && attachedFrame != null) { mTmpFrames.attachedFrame.set(attachedFrame); @@ -2768,7 +2789,7 @@ public final class ViewRootImpl implements ViewParent, * TODO(b/260382739): Apply this to all windows. */ private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) { - return lp.type == TYPE_NOTIFICATION_SHADE; + return (lp.privateFlags & PRIVATE_FLAG_OPTIMIZE_MEASURE) != 0; } private Rect getWindowBoundsInsetSystemBars() { @@ -5741,7 +5762,7 @@ public final class ViewRootImpl implements ViewParent, mTmpFrames.frame.right = l + w; mTmpFrames.frame.top = t; mTmpFrames.frame.bottom = t + h; - setFrame(mTmpFrames.frame); + setFrame(mTmpFrames.frame, false /* withinRelayout */); maybeHandleWindowMove(mWinFrame); } break; @@ -8211,7 +8232,7 @@ public final class ViewRootImpl implements ViewParent, // If the position and the size of the frame are both changed, it will trigger a BLAST // sync, and we still need to call relayout to obtain the syncSeqId. Otherwise, we just // need to send attributes via relayoutAsync. - final Rect oldFrame = mWinFrame; + final Rect oldFrame = mLastLayoutFrame; final Rect newFrame = mTmpFrames.frame; final boolean positionChanged = newFrame.top != oldFrame.top || newFrame.left != oldFrame.left; @@ -8341,7 +8362,7 @@ public final class ViewRootImpl implements ViewParent, params.restore(); } - setFrame(mTmpFrames.frame); + setFrame(mTmpFrames.frame, true /* withinRelayout */); return relayoutResult; } @@ -8376,8 +8397,18 @@ public final class ViewRootImpl implements ViewParent, mIsSurfaceOpaque = opaque; } - private void setFrame(Rect frame) { + /** + * Set the mWinFrame of this window. + * @param frame the new frame of this window. + * @param withinRelayout {@code true} if this setting is within the relayout, or is the initial + * setting. That will make sure in the relayout process, we always compare + * the window frame with the last processed window frame. + */ + private void setFrame(Rect frame, boolean withinRelayout) { mWinFrame.set(frame); + if (withinRelayout) { + mLastLayoutFrame.set(frame); + } final WindowConfiguration winConfig = getCompatWindowConfiguration(); mPendingBackDropFrame.set(mPendingDragResizing && !winConfig.useWindowFrameForBackdrop() @@ -8412,6 +8443,9 @@ public final class ViewRootImpl implements ViewParent, */ void getDisplayFrame(Rect outFrame) { outFrame.set(mTmpFrames.displayFrame); + // Apply sandboxing here (in getter) due to possible layout updates on the client after + // mTmpFrames.displayFrame is received from the server. + applyViewBoundsSandboxingIfNeeded(outFrame); } /** @@ -8428,6 +8462,69 @@ public final class ViewRootImpl implements ViewParent, outFrame.top += insets.top; outFrame.right -= insets.right; outFrame.bottom -= insets.bottom; + // Apply sandboxing here (in getter) due to possible layout updates on the client after + // mTmpFrames.displayFrame is received from the server. + applyViewBoundsSandboxingIfNeeded(outFrame); + } + + /** + * Offset outRect to make it sandboxed within Window's bounds. + * + *

    This is used by {@link android.view.View#getBoundsOnScreen}, + * {@link android.view.ViewRootImpl#getDisplayFrame} and + * {@link android.view.ViewRootImpl#getWindowVisibleDisplayFrame}, which are invoked by + * {@link android.view.View#getWindowDisplayFrame} and + * {@link android.view.View#getWindowVisibleDisplayFrame}, as well as + * {@link android.view.ViewDebug#captureLayers} for debugging. + */ + void applyViewBoundsSandboxingIfNeeded(final Rect inOutRect) { + if (mViewBoundsSandboxingEnabled) { + final Rect bounds = getConfiguration().windowConfiguration.getBounds(); + inOutRect.offset(-bounds.left, -bounds.top); + } + } + + /** + * Offset outLocation to make it sandboxed within Window's bounds. + * + *

    This is used by {@link android.view.View#getLocationOnScreen(int[])} + */ + public void applyViewLocationSandboxingIfNeeded(@Size(2) int[] outLocation) { + if (mViewBoundsSandboxingEnabled) { + final Rect bounds = getConfiguration().windowConfiguration.getBounds(); + outLocation[0] -= bounds.left; + outLocation[1] -= bounds.top; + } + } + + private boolean getViewBoundsSandboxingEnabled() { + // System dialogs (e.g. ANR) can be created within System process, so handleBindApplication + // may be never called. This results into all app compat changes being enabled + // (see b/268007823) because AppCompatCallbacks.install() is never called with non-empty + // array. + // With ActivityThread.isSystem we verify that it is not the system process, + // then this CompatChange can take effect. + if (ActivityThread.isSystem() + || !CompatChanges.isChangeEnabled(OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS)) { + // It is a system process or OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS change-id is disabled. + return false; + } + + // OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS is enabled by the device manufacturer. + try { + final List properties = mContext.getPackageManager() + .queryApplicationProperty(PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS); + + final boolean isOptedOut = !properties.isEmpty() && !properties.get(0).getBoolean(); + if (isOptedOut) { + // PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS is disabled by the app devs. + return false; + } + } catch (RuntimeException e) { + // remote exception. + } + + return true; } /** @@ -8618,6 +8715,8 @@ public final class ViewRootImpl implements ViewParent, mInsetsController.dump(prefix, writer); + mOnBackInvokedDispatcher.dump(prefix, writer); + writer.println(prefix + "View Hierarchy:"); dumpViewHierarchy(innerPrefix, writer, mView); } @@ -8749,6 +8848,10 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; AnimationHandler.removeRequestor(this); } + if (mSyncBufferCallback != null) { + mSyncBufferCallback.onBufferReady(null); + mSyncBufferCallback = null; + } WindowManagerGlobal.getInstance().doRemoveView(this); } @@ -9863,9 +9966,12 @@ public final class ViewRootImpl implements ViewParent, } void checkThread() { - if (mThread != Thread.currentThread()) { + Thread current = Thread.currentThread(); + if (mThread != current) { throw new CalledFromWrongThreadException( - "Only the original thread that created a view hierarchy can touch its views."); + "Only the original thread that created a view hierarchy can touch its views." + + " Expected: " + mThread.getName() + + " Calling: " + current.getName()); } } diff --git a/core/java/android/view/ViewTraversalTracingStrings.java b/core/java/android/view/ViewTraversalTracingStrings.java new file mode 100644 index 0000000000000000000000000000000000000000..7dde87bad26e38983ae6dd9805522935cb443d06 --- /dev/null +++ b/core/java/android/view/ViewTraversalTracingStrings.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.os.Trace; + +/** + * Keeps and caches strings used to trace {@link View} traversals. + *

    + * This is done to avoid expensive computations of them every time, which can improve performance. + */ +class ViewTraversalTracingStrings { + + /** {@link Trace} tag used to mark {@link View#onMeasure(int, int)}. */ + public final String onMeasure; + + /** {@link Trace} tag used to mark {@link View#onLayout(boolean, int, int, int, int)}. */ + public final String onLayout; + + /** Caches the view simple name to avoid re-computations. */ + public final String classSimpleName; + + /** Prefix for request layout stacktraces output in logs. */ + public final String requestLayoutStacktracePrefix; + + /** {@link Trace} tag used to mark {@link View#onMeasure(int, int)} happening before layout. */ + public final String onMeasureBeforeLayout; + + /** + * @param v {@link View} from where to get the class name. + */ + ViewTraversalTracingStrings(View v) { + String className = v.getClass().getSimpleName(); + classSimpleName = className; + onMeasureBeforeLayout = getTraceName("onMeasureBeforeLayout", className, v); + onMeasure = getTraceName("onMeasure", className, v); + onLayout = getTraceName("onLayout", className, v); + requestLayoutStacktracePrefix = "requestLayout " + className; + } + + private String getTraceName(String sectionName, String className, View v) { + StringBuilder out = new StringBuilder(); + out.append(sectionName); + out.append(" "); + out.append(className); + v.appendId(out); + return out.substring(0, Math.min(out.length() - 1, Trace.MAX_SECTION_NAME_LEN - 1)); + } +} diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 02027e4a39693280de3cfa8f608217ae3d03db9b..293f9082670d7cf33163822657164e8466e2d570 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -823,6 +823,11 @@ public abstract class Window { /** @hide */ public final void destroy() { mDestroyed = true; + onDestroy(); + } + + /** @hide */ + protected void onDestroy() { } /** @hide */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index ed9cb00db290d447eb1c35d3cd11d0f3d24d7b4b..033f7263bfc6e2d4063bcdd20aaa4bec10bbd9ad 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -814,8 +814,8 @@ public interface WindowManager extends ViewManager { } /** - * Activity level {@link android.content.pm.PackageManager.Property PackageManager - * .Property} for an app to inform the system that the activity can be opted-in or opted-out + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the app can be opted-in or opted-out * from the compatibility treatment that avoids {@link * android.app.Activity#setRequestedOrientation} loops. The loop can be trigerred by * ignoreRequestedOrientation display setting enabled on the device or by the landscape natural @@ -833,17 +833,17 @@ public interface WindowManager extends ViewManager { *

  • Camera compatibility force rotation treatment is active for the package. * * - *

    Setting this property to {@code false} informs the system that the activity must be + *

    Setting this property to {@code false} informs the system that the app must be * opted-out from the compatibility treatment even if the device manufacturer has opted the app * into the treatment. * *

    Syntax: *

    -     * <activity>
    +     * <application>
          *   <property
          *     android:name="android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"
          *     android:value="true|false"/>
    -     * </activity>
    +     * </application>
          * 
    * * @hide @@ -852,6 +852,323 @@ public interface WindowManager extends ViewManager { String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION"; + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager.Property} + * for an app to inform the system that the app can be opted-out from the compatibility + * treatment that avoids {@link android.app.Activity#setRequestedOrientation} loops. The loop + * can be trigerred by ignoreRequestedOrientation display setting enabled on the device or + * by the landscape natural orientation of the device. + * + *

    The system could ignore {@link android.app.Activity#setRequestedOrientation} + * call from an app if both of the following conditions are true: + *

      + *
    • Activity has requested orientation more than 2 times within 1-second timer + *
    • Activity is not letterboxed for fixed orientation + *
    + * + *

    Setting this property to {@code false} informs the system that the app must be + * opted-out from the compatibility treatment even if the device manufacturer has opted the app + * into the treatment. + * + *

    Not setting this property at all, or setting this property to {@code true} has no effect. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name=
    +     *       "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"
    +     *     android:value="false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/274924641): Make this public API. + String PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = + "android.window.PROPERTY_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that it needs to be opted-out from the + * compatibility treatment that sandboxes {@link android.view.View} API. + * + *

    The treatment can be enabled by device manufacturers for applications which misuse + * {@link android.view.View} APIs by expecting that + * {@link android.view.View#getLocationOnScreen}, + * {@link android.view.View#getBoundsOnScreen}, + * {@link android.view.View#getWindowVisibleDisplayFrame}, + * {@link android.view.View#getWindowDisplayFrame} + * return coordinates as if an activity is positioned in the top-left corner of the screen, with + * left coordinate equal to 0. This may not be the case for applications in multi-window and in + * letterbox modes. + * + *

    Setting this property to {@code false} informs the system that the application must be + * opted-out from the "Sandbox {@link android.view.View} API to Activity bounds" treatment even + * if the device manufacturer has opted the app into the treatment. + * + *

    Not setting this property at all, or setting this property to {@code true} has no effect. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"
    +     *     android:value="false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = + "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the application can be opted-in or opted-out + * from the compatibility treatment that enables sending a fake focus event for unfocused + * resumed split screen activities. This is needed because some game engines wait to get + * focus before drawing the content of the app which isn't guaranteed by default in multi-window + * modes. + * + *

    Device manufacturers can enable this treatment using their discretion on a per-device + * basis to improve display compatibility. The treatment also needs to be specifically enabled + * on a per-app basis afterwards. This can either be done by device manufacturers or developers. + * + *

    With this property set to {@code true}, the system will apply the treatment only if the + * device manufacturer had previously enabled it on the device. A fake focus event will be sent + * to the app after it is resumed only if the app is in split-screen. + * + *

    Setting this property to {@code false} informs the system that the activity must be + * opted-out from the compatibility treatment even if the device manufacturer has opted the app + * into the treatment. + * + *

    If the property remains unset the system will apply the treatment only if it had + * previously been enabled both at the device and app level by the device manufacturer. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the app should be excluded from the + * camera compatibility force rotation treatment. + * + *

    The camera compatibility treatment aligns orientations of portrait app window and natural + * orientation of the device and set opposite to natural orientation for a landscape app + * window. Mismatch between them can lead to camera issues like sideways or stretched + * viewfinder since this is one of the strongest assumptions that apps make when they implement + * camera previews. Since app and natural display orientations aren't guaranteed to match, the + * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to + * camera and is removed once camera is closed. + * + *

    The camera compatibility can be enabled by device manufacturers on the displays that have + * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed + * orientation, see Enhanced letterboxing + * for more details). + * + *

    With this property set to {@code true} or unset, the system may apply the force rotation + * treatment to fixed orientation activities. Device manufacturers can exclude packages from the + * treatment using their discretion to improve display compatibility. + * + *

    With this property set to {@code false}, the system will not apply the force rotation + * treatment. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = + "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the app should be excluded + * from the activity "refresh" after the camera compatibility force rotation treatment. + * + *

    The camera compatibility treatment aligns orientations of portrait app window and natural + * orientation of the device and set opposite to natural orientation for a landscape app + * window. Mismatch between them can lead to camera issues like sideways or stretched + * viewfinder since this is one of the strongest assumptions that apps make when they implement + * camera previews. Since app and natural display orientations aren't guaranteed to match, the + * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to + * camera and is removed once camera is closed. + * + *

    Force rotation is followed by the "Refresh" of the activity by going through "resumed -> + * ... -> stopped -> ... -> resumed" cycle (by default) or "resumed -> paused -> resumed" cycle + * (if overridden, see {@link #PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE} for context). + * This allows to clear cached values in apps (e.g. display or camera rotation) that influence + * camera preview and can lead to sideways or stretching issues persisting even after force + * rotation. + * + *

    The camera compatibility can be enabled by device manufacturers on the displays that have + * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed + * orientation, see Enhanced letterboxing + * for more details). + * + *

    With this property set to {@code true} or unset, the system may "refresh" activity after + * the force rotation treatment. Device manufacturers can exclude packages from the "refresh" + * using their discretion to improve display compatibility. + * + *

    With this property set to {@code false}, the system will not "refresh" activity after the + * force rotation treatment. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = + "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the activity should be or shouldn't be + * "refreshed" after the camera compatibility force rotation treatment using "paused -> + * resumed" cycle rather than "stopped -> resumed". + * + *

    The camera compatibility treatment aligns orientations of portrait app window and natural + * orientation of the device and set opposite to natural orientation for a landscape app + * window. Mismatch between them can lead to camera issues like sideways or stretched + * viewfinder since this is one of the strongest assumptions that apps make when they implement + * camera previews. Since app and natural display orientations aren't guaranteed to match, the + * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to + * camera and is removed once camera is closed. + * + *

    Force rotation is followed by the "Refresh" of the activity by going through "resumed -> + * ... -> stopped -> ... -> resumed" cycle (by default) or "resumed -> paused -> resumed" cycle + * (if overridden by device manufacturers or using this property). This allows to clear cached + * values in apps (e.g., display or camera rotation) that influence camera preview and can lead + * to sideways or stretching issues persisting even after force rotation. + * + *

    The camera compatibility can be enabled by device manufacturers on the displays that have + * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed + * orientation, see Enhanced letterboxing + * for more details). + * + *

    Device manufacturers can override packages to "refresh" via "resumed -> paused -> resumed" + * cycle using their discretion to improve display compatibility. + * + *

    With this property set to {@code true}, the system will "refresh" activity after the + * force rotation treatment using "resumed -> paused -> resumed" cycle. + * + *

    With this property set to {@code false}, the system will not "refresh" activity after the + * force rotation treatment using "resumed -> paused -> resumed" cycle even if the device + * manufacturer adds the corresponding override. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = + "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the app should be excluded from the + * compatibility override for orientation set by the device manufacturer. + * + *

    With this property set to {@code true} or unset, device manufacturers can override + * orientation for the app using their discretion to improve display compatibility. + * + *

    With this property set to {@code false}, device manufactured per-app override for + * orientation won't be applied. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = + "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"; + + /** + * Application level {@link android.content.pm.PackageManager.Property PackageManager + * .Property} for an app to inform the system that the app should be opted-out from the + * compatibility override that fixes display orientation to landscape natural orientation when + * an activity is fullscreen. + * + *

    When this compat override is enabled and while display is fixed to the landscape natural + * orientation, the orientation requested by the activity will be still respected by bounds + * resolution logic. For instance, if an activity requests portrait orientation, then activity + * will appear in the letterbox mode for fixed orientation with the display rotated to the + * lanscape natural orientation. + * + *

    The treatment is disabled by default but device manufacturers can enable the treatment + * using their discretion to improve display compatibility on the displays that have + * ignoreOrientationRequest display setting enabled (enables compatibility mode for fixed + * orientation, see Enhanced letterboxing + * for more details). + * + *

    With this property set to {@code true} or unset, the system wiil use landscape display + * orientation when the following conditions are met: + *

      + *
    • Natural orientation of the display is landscape + *
    • ignoreOrientationRequest display setting is enabled + *
    • Activity is fullscreen. + *
    • Device manufacturer enabled the treatment. + *
    + * + *

    With this property set to {@code false}, device manufactured per-app override for + * display orientation won't be applied. + * + *

    Syntax: + *

    +     * <application>
    +     *   <property
    +     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"
    +     *     android:value="true|false"/>
    +     * </application>
    +     * 
    + * + * @hide + */ + // TODO(b/263984287): Make this public API. + String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = + "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE"; + /** * @hide */ @@ -2442,6 +2759,15 @@ public interface WindowManager extends ViewManager { * {@hide} */ public static final int PRIVATE_FLAG_SYSTEM_ERROR = 0x00000100; + /** + * Flag to indicate that the view hierarchy of the window can only be measured when + * necessary. If a window size can be known by the LayoutParams, we can use the size to + * relayout window, and we don't have to measure the view hierarchy before laying out the + * views. This reduces the chances to perform measure. + * {@hide} + */ + public static final int PRIVATE_FLAG_OPTIMIZE_MEASURE = 0x00000200; + /** * Flag that prevents the wallpaper behind the current window from receiving touch events. * @@ -2644,6 +2970,7 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_NO_MOVE_ANIMATION, PRIVATE_FLAG_COMPATIBLE_WINDOW, PRIVATE_FLAG_SYSTEM_ERROR, + PRIVATE_FLAG_OPTIMIZE_MEASURE, PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS, PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR, PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT, @@ -2703,6 +3030,10 @@ public interface WindowManager extends ViewManager { mask = PRIVATE_FLAG_SYSTEM_ERROR, equals = PRIVATE_FLAG_SYSTEM_ERROR, name = "SYSTEM_ERROR"), + @ViewDebug.FlagToString( + mask = PRIVATE_FLAG_OPTIMIZE_MEASURE, + equals = PRIVATE_FLAG_OPTIMIZE_MEASURE, + name = "OPTIMIZE_MEASURE"), @ViewDebug.FlagToString( mask = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS, equals = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS, diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 59b5286f6fc54998af960977312052991c98fe83..34c7b8b9889f5e9306afd9c7c92f890b4a422e41 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -82,11 +82,19 @@ public final class ContentCaptureContext implements Parcelable { @SystemApi public static final int FLAG_RECONNECTED = 0x4; + /** + * Flag used to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final int FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING = 1 << 3; + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_DISABLED_BY_APP, FLAG_DISABLED_BY_FLAG_SECURE, - FLAG_RECONNECTED + FLAG_RECONNECTED, + FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING }) @Retention(RetentionPolicy.SOURCE) @interface ContextCreationFlags{} @@ -252,7 +260,8 @@ public final class ContentCaptureContext implements Parcelable { * Gets the flags associated with this context. * * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE}, - * {@link #FLAG_DISABLED_BY_APP} and {@link #FLAG_RECONNECTED}. + * {@link #FLAG_DISABLED_BY_APP}, {@link #FLAG_RECONNECTED} and {@link + * #FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING}. * * @hide */ diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index d067d4bc366b1ccf0b70ec5d25dfa85df272cf00..668351b949c1b5def308c1287f7fcf00f0eebf4e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -51,6 +51,7 @@ import android.view.WindowManager; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; @@ -66,8 +67,7 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - *

    The {@link ContentCaptureManager} provides additional ways for for apps to - * integrate with the content capture subsystem. + *

    Provides additional ways for apps to integrate with the content capture subsystem. * *

    Content capture provides real-time, continuous capture of application activity, display and * events to an intelligence service that is provided by the Android system. The intelligence @@ -344,6 +344,14 @@ public final class ContentCaptureManager { */ public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout"; + /** + * Sets to disable flush when receiving a VIEW_TREE_APPEARING event. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = + "disable_flush_for_view_tree_appearing"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -374,6 +382,8 @@ public final class ContentCaptureManager { public static final int DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS = 1_000; /** @hide */ public static final int DEFAULT_LOG_HISTORY_SIZE = 10; + /** @hide */ + public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false; private final Object mLock = new Object(); @@ -449,6 +459,7 @@ public final class ContentCaptureManager { mOptions = Objects.requireNonNull(options, "options cannot be null"); ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel); + setFlushViewTreeAppearingEventDisabled(mOptions.disableFlushForViewTreeAppearing); if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); @@ -687,6 +698,38 @@ public final class ContentCaptureManager { } } + /** + * Explicitly sets enable or disable flush for view tree appearing event. + * + * @hide + */ + @VisibleForTesting + public void setFlushViewTreeAppearingEventDisabled(boolean disabled) { + if (sDebug) { + Log.d(TAG, "setFlushViewTreeAppearingEventDisabled(): setting to " + disabled); + } + + synchronized (mLock) { + if (disabled) { + mFlags |= ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } else { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING; + } + } + } + + /** + * Gets whether content capture is needed to flush for view tree appearing event. + * + * @hide + */ + public boolean getFlushViewTreeAppearingEventDisabled() { + synchronized (mLock) { + return (mFlags & ContentCaptureContext.FLAG_DISABLED_FLUSH_FOR_VIEW_TREE_APPEARING) + != 0; + } + } + /** * Gets whether content capture is enabled for the given user. * diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 2134d819943e31c744c8811eff8b886272601d99..bdec1970eda9f86058765762733326c6697db9e9 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -170,6 +170,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int FLUSH_REASON_TEXT_CHANGE_TIMEOUT = 6; /** @hide */ public static final int FLUSH_REASON_SESSION_CONNECTED = 7; + /** @hide */ + public static final int FLUSH_REASON_FORCE_FLUSH = 8; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARING = 9; + /** @hide */ + public static final int FLUSH_REASON_VIEW_TREE_APPEARED = 10; /** @hide */ @IntDef(prefix = { "FLUSH_REASON_" }, value = { @@ -179,7 +185,10 @@ public abstract class ContentCaptureSession implements AutoCloseable { FLUSH_REASON_SESSION_FINISHED, FLUSH_REASON_IDLE_TIMEOUT, FLUSH_REASON_TEXT_CHANGE_TIMEOUT, - FLUSH_REASON_SESSION_CONNECTED + FLUSH_REASON_SESSION_CONNECTED, + FLUSH_REASON_FORCE_FLUSH, + FLUSH_REASON_VIEW_TREE_APPEARING, + FLUSH_REASON_VIEW_TREE_APPEARED }) @Retention(RetentionPolicy.SOURCE) public @interface FlushReason{} @@ -614,6 +623,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "TEXT_CHANGE"; case FLUSH_REASON_SESSION_CONNECTED: return "CONNECTED"; + case FLUSH_REASON_FORCE_FLUSH: + return "FORCE_FLUSH"; + case FLUSH_REASON_VIEW_TREE_APPEARING: + return "VIEW_TREE_APPEARING"; + case FLUSH_REASON_VIEW_TREE_APPEARED: + return "VIEW_TREE_APPEARED"; default: return "UNKOWN-" + reason; } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 1f5e462d71fd81291d1fb5453d495963285eac59..9848acd8dfcc57409dce3e1bcc4b9a696e9050e5 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -458,6 +458,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { case ContentCaptureEvent.TYPE_SESSION_FINISHED: flushReason = FLUSH_REASON_SESSION_FINISHED; break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARING; + break; + case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED: + flushReason = FLUSH_REASON_VIEW_TREE_APPEARED; + break; default: flushReason = FLUSH_REASON_FULL; } @@ -764,7 +770,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { /** Public because is also used by ViewRootImpl */ public void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; - mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH)); + final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled(); + + mHandler.post(() -> sendEvent( + new ContentCaptureEvent(sessionId, type), + disableFlush ? !started : FORCE_FLUSH)); } void notifySessionResumed(int sessionId) { diff --git a/core/java/android/webkit/WebResourceError.java b/core/java/android/webkit/WebResourceError.java index 11f1b6f175669b6f2336b97b15d67621495c7d42..4c874892d57633d97a5187d3982474c6347566ae 100644 --- a/core/java/android/webkit/WebResourceError.java +++ b/core/java/android/webkit/WebResourceError.java @@ -19,7 +19,7 @@ package android.webkit; import android.annotation.SystemApi; /** - * Encapsulates information about errors occured during loading of web resources. See + * Encapsulates information about errors that occurred during loading of web resources. See * {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, WebResourceError) WebViewClient.onReceivedError(WebView, WebResourceRequest, WebResourceError)} */ public abstract class WebResourceError { diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index 1024e2e50c3e2f5e106fd68a8aa2c1c82b8074bc..940b133eb16963912f0559e16b7a178f1373126f 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -18,10 +18,8 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import android.view.RemoteAnimationTarget; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -52,8 +50,6 @@ public class BackEvent implements Parcelable { @SwipeEdge private final int mSwipeEdge; - @Nullable - private final RemoteAnimationTarget mDepartingAnimationTarget; /** * Creates a new {@link BackEvent} instance. @@ -62,16 +58,12 @@ public class BackEvent implements Parcelable { * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param swipeEdge Indicates which edge the swipe starts from. - * @param departingAnimationTarget The remote animation target of the departing application - * window. */ - public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge, - @Nullable RemoteAnimationTarget departingAnimationTarget) { + public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; - mDepartingAnimationTarget = departingAnimationTarget; } private BackEvent(@NonNull Parcel in) { @@ -79,7 +71,6 @@ public class BackEvent implements Parcelable { mTouchY = in.readFloat(); mProgress = in.readFloat(); mSwipeEdge = in.readInt(); - mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); } public static final Creator CREATOR = new Creator() { @@ -105,11 +96,24 @@ public class BackEvent implements Parcelable { dest.writeFloat(mTouchY); dest.writeFloat(mProgress); dest.writeInt(mSwipeEdge); - dest.writeTypedObject(mDepartingAnimationTarget, flags); } /** - * Returns a value between 0 and 1 on how far along the back gesture is. + * Returns a value between 0 and 1 on how far along the back gesture is. This value is + * driven by the horizontal location of the touch point, and should be used as the fraction to + * seek the predictive back animation with. Specifically, + *

      + *
    1. The progress is 0 when the touch is at the starting edge of the screen (left or right), + * and animation should seek to its start state. + *
    2. The progress is approximately 1 when the touch is at the opposite side of the screen, + * and animation should seek to its end state. Exact end value may vary depending on + * screen size. + *
    + *
  • After the gesture finishes in cancel state, this method keeps getting invoked until the + * progress value animates back to 0. + * + * In-between locations are linearly interpolated based on horizontal distance from the starting + * edge and smooth clamped to 1 when the distance exceeds a system-wide threshold. */ public float getProgress() { return mProgress; @@ -136,16 +140,6 @@ public class BackEvent implements Parcelable { return mSwipeEdge; } - /** - * Returns the {@link RemoteAnimationTarget} of the top departing application window, - * or {@code null} if the top window should not be moved for the current type of back - * destination. - */ - @Nullable - public RemoteAnimationTarget getDepartingAnimationTarget() { - return mDepartingAnimationTarget; - } - @Override public String toString() { return "BackEvent{" @@ -153,7 +147,6 @@ public class BackEvent implements Parcelable { + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress + ", mSwipeEdge" + mSwipeEdge - + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + "}"; } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/core/java/android/window/BackMotionEvent.aidl similarity index 66% rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt rename to core/java/android/window/BackMotionEvent.aidl index 67733e90526837fb9b32200637d14704b583ff80..7c675c35c073ed2c2ff9f3df53d80ac1f1e433df 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt +++ b/core/java/android/window/BackMotionEvent.aidl @@ -11,15 +11,12 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.keyguard.shared.model -import kotlin.time.Duration -import kotlin.time.Duration.Companion.milliseconds +package android.window; -/** Animation parameters */ -data class AnimationParams( - val startTime: Duration = 0.milliseconds, - val duration: Duration, -) +/** + * @hide + */ +parcelable BackMotionEvent; diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8012a1c26bacaf5ffcd71ac70209b505e16272ff --- /dev/null +++ b/core/java/android/window/BackMotionEvent.java @@ -0,0 +1,150 @@ +/* + * 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 android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.RemoteAnimationTarget; + +/** + * Object used to report back gesture progress. Holds information about a {@link BackEvent} plus + * any {@link RemoteAnimationTarget} the gesture manipulates. + * + * @see BackEvent + * @hide + */ +public final class BackMotionEvent implements Parcelable { + private final float mTouchX; + private final float mTouchY; + private final float mProgress; + + @BackEvent.SwipeEdge + private final int mSwipeEdge; + @Nullable + private final RemoteAnimationTarget mDepartingAnimationTarget; + + /** + * Creates a new {@link BackMotionEvent} instance. + * + * @param touchX Absolute X location of the touch point of this event. + * @param touchY Absolute Y location of the touch point of this event. + * @param progress Value between 0 and 1 on how far along the back gesture is. + * @param swipeEdge Indicates which edge the swipe starts from. + * @param departingAnimationTarget The remote animation target of the departing + * application window. + */ + public BackMotionEvent(float touchX, float touchY, float progress, + @BackEvent.SwipeEdge int swipeEdge, + @Nullable RemoteAnimationTarget departingAnimationTarget) { + mTouchX = touchX; + mTouchY = touchY; + mProgress = progress; + mSwipeEdge = swipeEdge; + mDepartingAnimationTarget = departingAnimationTarget; + } + + private BackMotionEvent(@NonNull Parcel in) { + mTouchX = in.readFloat(); + mTouchY = in.readFloat(); + mProgress = in.readFloat(); + mSwipeEdge = in.readInt(); + mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); + } + + @NonNull + public static final Creator CREATOR = new Creator() { + @Override + public BackMotionEvent createFromParcel(Parcel in) { + return new BackMotionEvent(in); + } + + @Override + public BackMotionEvent[] newArray(int size) { + return new BackMotionEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeFloat(mTouchX); + dest.writeFloat(mTouchY); + dest.writeFloat(mProgress); + dest.writeInt(mSwipeEdge); + dest.writeTypedObject(mDepartingAnimationTarget, flags); + } + + /** + * Returns the progress of a {@link BackEvent}. + * + * @see BackEvent#getProgress() + */ + @FloatRange(from = 0, to = 1) + public float getProgress() { + return mProgress; + } + + /** + * Returns the absolute X location of the touch point. + */ + public float getTouchX() { + return mTouchX; + } + + /** + * Returns the absolute Y location of the touch point. + */ + public float getTouchY() { + return mTouchY; + } + + /** + * Returns the screen edge that the swipe starts from. + */ + @BackEvent.SwipeEdge + public int getSwipeEdge() { + return mSwipeEdge; + } + + /** + * Returns the {@link RemoteAnimationTarget} of the top departing application window, + * or {@code null} if the top window should not be moved for the current type of back + * destination. + */ + @Nullable + public RemoteAnimationTarget getDepartingAnimationTarget() { + return mDepartingAnimationTarget; + } + + @Override + public String toString() { + return "BackMotionEvent{" + + "mTouchX=" + mTouchX + + ", mTouchY=" + mTouchY + + ", mProgress=" + mProgress + + ", mSwipeEdge" + mSwipeEdge + + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + + "}"; + } +} diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index dd4385c8f50c3c8827a0e15f0496dbbe1b1fe869..b22f967e9e2a9eb19d39fe64d7945bea8acabfbc 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -16,8 +16,10 @@ package android.window; +import android.annotation.NonNull; import android.util.FloatProperty; +import com.android.internal.dynamicanimation.animation.DynamicAnimation; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; @@ -40,7 +42,7 @@ public class BackProgressAnimator { private final SpringAnimation mSpring; private ProgressCallback mCallback; private float mProgress = 0; - private BackEvent mLastBackEvent; + private BackMotionEvent mLastBackEvent; private boolean mStarted = false; private void setProgress(float progress) { @@ -82,9 +84,9 @@ public class BackProgressAnimator { /** * Sets a new target position for the back progress. * - * @param event the {@link BackEvent} containing the latest target progress. + * @param event the {@link BackMotionEvent} containing the latest target progress. */ - public void onBackProgressed(BackEvent event) { + public void onBackProgressed(BackMotionEvent event) { if (!mStarted) { return; } @@ -95,11 +97,11 @@ public class BackProgressAnimator { /** * Starts the back progress animation. * - * @param event the {@link BackEvent} that started the gesture. + * @param event the {@link BackMotionEvent} that started the gesture. * @param callback the back callback to invoke for the gesture. It will receive back progress * dispatches as the progress animation updates. */ - public void onBackStarted(BackEvent event, ProgressCallback callback) { + public void onBackStarted(BackMotionEvent event, ProgressCallback callback) { reset(); mLastBackEvent = event; mCallback = callback; @@ -123,14 +125,34 @@ public class BackProgressAnimator { mProgress = 0; } + /** + * Animate the back progress animation from current progress to start position. + * This should be called when back is cancelled. + * + * @param finishCallback the callback to be invoked when the progress is reach to 0. + */ + public void onBackCancelled(@NonNull Runnable finishCallback) { + final DynamicAnimation.OnAnimationEndListener listener = + new DynamicAnimation.OnAnimationEndListener() { + @Override + public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, + float velocity) { + mSpring.removeEndListener(this); + finishCallback.run(); + reset(); + } + }; + mSpring.addEndListener(listener); + mSpring.animateToFinalPosition(0); + } + private void updateProgressValue(float progress) { if (mLastBackEvent == null || mCallback == null || !mStarted) { return; } mCallback.onProgressUpdate( new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), - progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(), - mLastBackEvent.getDepartingAnimationTarget())); + progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge())); } } diff --git a/core/java/android/window/IOnBackInvokedCallback.aidl b/core/java/android/window/IOnBackInvokedCallback.aidl index 6af8ddda3a62e30ca24d4aa301c05a8e4cc16279..159c0e8afed00b4378528fcd10a8655e67295a33 100644 --- a/core/java/android/window/IOnBackInvokedCallback.aidl +++ b/core/java/android/window/IOnBackInvokedCallback.aidl @@ -17,7 +17,7 @@ package android.window; -import android.window.BackEvent; +import android.window.BackMotionEvent; /** * Interface that wraps a {@link OnBackInvokedCallback} object, to be stored in window manager @@ -30,18 +30,19 @@ oneway interface IOnBackInvokedCallback { * Called when a back gesture has been started, or back button has been pressed down. * Wraps {@link OnBackInvokedCallback#onBackStarted(BackEvent)}. * - * @param backEvent The {@link BackEvent} containing information about the touch or button press. + * @param backMotionEvent The {@link BackMotionEvent} containing information about the touch + * or button press. */ - void onBackStarted(in BackEvent backEvent); + void onBackStarted(in BackMotionEvent backMotionEvent); /** * Called on back gesture progress. * Wraps {@link OnBackInvokedCallback#onBackProgressed(BackEvent)}. * - * @param backEvent The {@link BackEvent} containing information about the latest touch point - * and the progress that the back animation should seek to. + * @param backMotionEvent The {@link BackMotionEvent} containing information about the latest + * touch point and the progress that the back animation should seek to. */ - void onBackProgressed(in BackEvent backEvent); + void onBackProgressed(in BackMotionEvent backMotionEvent); /** * Called when a back gesture or back button press has been cancelled. diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index e6bb1f64ad861b09eca164fa7a5c9d7f746422ee..e10f7c838c744241e13adb5b677c5d5e9f3485e3 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -40,7 +40,8 @@ interface ITaskOrganizerController { void unregisterTaskOrganizer(ITaskOrganizer organizer); /** Creates a persistent root task in WM for a particular windowing-mode. */ - void createRootTask(int displayId, int windowingMode, IBinder launchCookie); + void createRootTask(int displayId, int windowingMode, IBinder launchCookie, + boolean removeWithTaskOrganizer); /** Deletes a persistent root task in WM */ boolean deleteRootTask(in WindowContainerToken task); @@ -72,11 +73,17 @@ interface ITaskOrganizerController { /** * Controls whether ignore orientation request logic in {@link - * com.android.server.wm.DisplayArea} is disabled at runtime. + * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some + * requested orientations to others. * * @param isDisabled when {@code true}, the system always ignores the value of {@link * com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app * requested orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index */ - void setIsIgnoreOrientationRequestDisabled(boolean isDisabled); + void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + in int[] fromOrientations, in int[] toOrientations); } diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index a0bd7f70ca585747482e5c80be62631e2eeec44e..34b75a4788c40c9746df7e869d2c6f44ede00776 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -211,6 +211,12 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc IOnBackInvokedCallback getIOnBackInvokedCallback() { return mIOnBackInvokedCallback; } + + @Override + public String toString() { + return "ImeCallback=ImeOnBackInvokedCallback@" + mId + + " Callback=" + mIOnBackInvokedCallback; + } } /** diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java index 49acde9dc2953201d20bfa552a91852bebb28ba7..eb3bcaec003a380410c416363a9b813f97759fd1 100644 --- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -179,16 +179,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return; } clearCallbacksOnDispatcher(); - if (actualDispatcher instanceof ProxyOnBackInvokedDispatcher) { - // We don't want to nest ProxyDispatchers, so if we are given on, we unwrap its - // actual dispatcher. - // This can happen when an Activity is recreated but the Window is preserved (e.g. - // when going from split-screen back to single screen) - mActualDispatcher = - ((ProxyOnBackInvokedDispatcher) actualDispatcher).mActualDispatcher; - } else { - mActualDispatcher = actualDispatcher; - } + mActualDispatcher = actualDispatcher; transferCallbacksToDispatcher(); } } diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java index 12ad914986264d38f55cebd58c6cbec0a436122a..c8f632707966b03cefe6c163f646d77c6cd2a38c 100644 --- a/core/java/android/window/TaskFragmentAnimationParams.java +++ b/core/java/android/window/TaskFragmentAnimationParams.java @@ -33,6 +33,13 @@ public final class TaskFragmentAnimationParams implements Parcelable { public static final TaskFragmentAnimationParams DEFAULT = new TaskFragmentAnimationParams.Builder().build(); + /** + * The default value for animation background color, which means to use the theme window + * background color. + */ + @ColorInt + public static final int DEFAULT_ANIMATION_BACKGROUND_COLOR = 0; + @ColorInt private final int mAnimationBackgroundColor; @@ -104,12 +111,13 @@ public final class TaskFragmentAnimationParams implements Parcelable { public static final class Builder { @ColorInt - private int mAnimationBackgroundColor = 0; + private int mAnimationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR; /** * Sets the {@link ColorInt} to use for the background during the animation with this * TaskFragment if the animation requires a background. The default value is - * {@code 0}, which is to use the theme window background. + * {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}, which is to use the theme window background + * color. * * @param color a packed color int, {@code AARRGGBB}, for the animation background color. * @return this {@link Builder}. diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java index c9ddf92d37407cbc90dd0e7a8536bd1d03e8ca3e..203d79aad7a37327b83893d9610064387133557e 100644 --- a/core/java/android/window/TaskFragmentCreationParams.java +++ b/core/java/android/window/TaskFragmentCreationParams.java @@ -71,20 +71,42 @@ public final class TaskFragmentCreationParams implements Parcelable { * * This is needed in case we need to launch a placeholder Activity to split below a transparent * always-expand Activity. + * + * This should not be used with {@link #mPairedActivityToken}. */ @Nullable private final IBinder mPairedPrimaryFragmentToken; + /** + * The Activity token to place the new TaskFragment on top of. + * When it is set, the new TaskFragment will be positioned right above the target Activity. + * Otherwise, the new TaskFragment will be positioned on the top of the Task by default. + * + * This is needed in case we need to place an Activity into TaskFragment to launch placeholder + * below a transparent always-expand Activity, or when there is another Intent being started in + * a TaskFragment above. + * + * This should not be used with {@link #mPairedPrimaryFragmentToken}. + */ + @Nullable + private final IBinder mPairedActivityToken; + private TaskFragmentCreationParams( @NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect initialBounds, - @WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken) { + @WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken, + @Nullable IBinder pairedActivityToken) { + if (pairedPrimaryFragmentToken != null && pairedActivityToken != null) { + throw new IllegalArgumentException("pairedPrimaryFragmentToken and" + + " pairedActivityToken should not be set at the same time."); + } mOrganizer = organizer; mFragmentToken = fragmentToken; mOwnerToken = ownerToken; mInitialBounds.set(initialBounds); mWindowingMode = windowingMode; mPairedPrimaryFragmentToken = pairedPrimaryFragmentToken; + mPairedActivityToken = pairedActivityToken; } @NonNull @@ -121,6 +143,15 @@ public final class TaskFragmentCreationParams implements Parcelable { return mPairedPrimaryFragmentToken; } + /** + * TODO(b/232476698): remove the hide with adding CTS for this in next release. + * @hide + */ + @Nullable + public IBinder getPairedActivityToken() { + return mPairedActivityToken; + } + private TaskFragmentCreationParams(Parcel in) { mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in); mFragmentToken = in.readStrongBinder(); @@ -128,6 +159,7 @@ public final class TaskFragmentCreationParams implements Parcelable { mInitialBounds.readFromParcel(in); mWindowingMode = in.readInt(); mPairedPrimaryFragmentToken = in.readStrongBinder(); + mPairedActivityToken = in.readStrongBinder(); } /** @hide */ @@ -139,6 +171,7 @@ public final class TaskFragmentCreationParams implements Parcelable { mInitialBounds.writeToParcel(dest, flags); dest.writeInt(mWindowingMode); dest.writeStrongBinder(mPairedPrimaryFragmentToken); + dest.writeStrongBinder(mPairedActivityToken); } @NonNull @@ -164,6 +197,7 @@ public final class TaskFragmentCreationParams implements Parcelable { + " initialBounds=" + mInitialBounds + " windowingMode=" + mWindowingMode + " pairedFragmentToken=" + mPairedPrimaryFragmentToken + + " pairedActivityToken=" + mPairedActivityToken + "}"; } @@ -194,6 +228,9 @@ public final class TaskFragmentCreationParams implements Parcelable { @Nullable private IBinder mPairedPrimaryFragmentToken; + @Nullable + private IBinder mPairedActivityToken; + public Builder(@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) { mOrganizer = organizer; @@ -224,6 +261,8 @@ public final class TaskFragmentCreationParams implements Parcelable { * This is needed in case we need to launch a placeholder Activity to split below a * transparent always-expand Activity. * + * This should not be used with {@link #setPairedActivityToken}. + * * TODO(b/232476698): remove the hide with adding CTS for this in next release. * @hide */ @@ -233,11 +272,32 @@ public final class TaskFragmentCreationParams implements Parcelable { return this; } + /** + * Sets the Activity token to place the new TaskFragment on top of. + * When it is set, the new TaskFragment will be positioned right above the target Activity. + * Otherwise, the new TaskFragment will be positioned on the top of the Task by default. + * + * This is needed in case we need to place an Activity into TaskFragment to launch + * placeholder below a transparent always-expand Activity, or when there is another Intent + * being started in a TaskFragment above. + * + * This should not be used with {@link #setPairedPrimaryFragmentToken}. + * + * TODO(b/232476698): remove the hide with adding CTS for this in next release. + * @hide + */ + @NonNull + public Builder setPairedActivityToken(@Nullable IBinder activityToken) { + mPairedActivityToken = activityToken; + return this; + } + /** Constructs the options to create TaskFragment with. */ @NonNull public TaskFragmentCreationParams build() { return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken, - mInitialBounds, mWindowingMode, mPairedPrimaryFragmentToken); + mInitialBounds, mWindowingMode, mPairedPrimaryFragmentToken, + mPairedActivityToken); } } } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index bffd4e437dfac18f8deca7c87e28da867d2784c1..3aa9941d24b79f895948ce7a06f20e2a7586074f 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -152,17 +152,33 @@ public class TaskOrganizer extends WindowOrganizer { * @param windowingMode Windowing mode to put the root task in. * @param launchCookie Launch cookie to associate with the task so that is can be identified * when the {@link ITaskOrganizer#onTaskAppeared} callback is called. + * @param removeWithTaskOrganizer True if this task should be removed when organizer destroyed. + * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - @Nullable - public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) { + public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie, + boolean removeWithTaskOrganizer) { try { - mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie); + mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie, + removeWithTaskOrganizer); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + /** + * Creates a persistent root task in WM for a particular windowing-mode. + * @param displayId The display to create the root task on. + * @param windowingMode Windowing mode to put the root task in. + * @param launchCookie Launch cookie to associate with the task so that is can be identified + * when the {@link ITaskOrganizer#onTaskAppeared} callback is called. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @Nullable + public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) { + createRootTask(displayId, windowingMode, launchCookie, false /* removeWithTaskOrganizer */); + } + /** Deletes a persistent root task in WM */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean deleteRootTask(@NonNull WindowContainerToken task) { @@ -254,17 +270,24 @@ public class TaskOrganizer extends WindowOrganizer { /** * Controls whether ignore orientation request logic in {@link - * com.android.server.wm.DisplayArea} is disabled at runtime. + * com.android.server.wm.DisplayArea} is disabled at runtime and how to optionally map some + * requested orientation to others. * - * @param isDisabled when {@code true}, the system always ignores the value of {@link - * com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} and app - * requested orientation is respected. + * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the + * value of {@link com.android.server.wm.DisplayArea#getIgnoreOrientationRequest} + * and app requested orientation is respected. + * @param fromOrientations The orientations we want to map to the correspondent orientations + * in toOrientation. + * @param toOrientations The orientations we map to the ones in fromOrientations at the same + * index * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public void setIsIgnoreOrientationRequestDisabled(boolean isDisabled) { + public void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled, + @Nullable int[] fromOrientations, @Nullable int[] toOrientations) { try { - mTaskOrganizerController.setIsIgnoreOrientationRequestDisabled(isDisabled); + mTaskOrganizerController.setOrientationRequestPolicy(isIgnoreOrientationRequestDisabled, + fromOrientations, toOrientations); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 9aba5a49129575d354ffa7c59281e07fd39b81ea..257c225e3386e232e8739350b8c0c0c08df3c869 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -121,6 +121,19 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + /** + * Sets the densityDpi value in the configuration for the given container. + * @hide + */ + @NonNull + public WindowContainerTransaction setDensityDpi(@NonNull WindowContainerToken container, + int densityDpi) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.densityDpi = densityDpi; + chg.mConfigSetMask |= ActivityInfo.CONFIG_DENSITY; + return this; + } + /** * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task * has finished the enter animation with the given bounds. diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index fda39c14dac74c4af23c9d5825456a80af8517aa..caec4bca9469b1d76e9de995b0852b3e33577ba0 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -27,6 +27,7 @@ import android.util.Log; import android.view.IWindow; import android.view.IWindowSession; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; @@ -221,6 +222,26 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @NonNull private static final BackProgressAnimator mProgressAnimator = new BackProgressAnimator(); + /** + * Dump information about this WindowOnBackInvokedDispatcher + * @param prefix the prefix that will be prepended to each line of the produced output + * @param writer the writer that will receive the resulting text + */ + public void dump(String prefix, PrintWriter writer) { + String innerPrefix = prefix + " "; + writer.println(prefix + "WindowOnBackDispatcher:"); + if (mAllCallbacks.isEmpty()) { + writer.println(prefix + ""); + return; + } + + writer.println(innerPrefix + "Top Callback: " + getTopCallback()); + writer.println(innerPrefix + "Callbacks: "); + mAllCallbacks.forEach((callback, priority) -> { + writer.println(innerPrefix + " Callback: " + callback + " Priority=" + priority); + }); + } + static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference mCallback; @@ -229,19 +250,21 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } @Override - public void onBackStarted(BackEvent backEvent) { + public void onBackStarted(BackMotionEvent backEvent) { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { mProgressAnimator.onBackStarted(backEvent, event -> callback.onBackProgressed(event)); - callback.onBackStarted(backEvent); + callback.onBackStarted(new BackEvent( + backEvent.getTouchX(), backEvent.getTouchY(), + backEvent.getProgress(), backEvent.getSwipeEdge())); } }); } @Override - public void onBackProgressed(BackEvent backEvent) { + public void onBackProgressed(BackMotionEvent backEvent) { Handler.getMain().post(() -> { final OnBackAnimationCallback callback = getBackAnimationCallback(); if (callback != null) { @@ -253,11 +276,12 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackCancelled() { Handler.getMain().post(() -> { - mProgressAnimator.reset(); - final OnBackAnimationCallback callback = getBackAnimationCallback(); - if (callback != null) { - callback.onBackCancelled(); - } + mProgressAnimator.onBackCancelled(() -> { + final OnBackAnimationCallback callback = getBackAnimationCallback(); + if (callback != null) { + callback.onBackCancelled(); + } + }); }); } @@ -267,6 +291,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mProgressAnimator.reset(); final OnBackInvokedCallback callback = mCallback.get(); if (callback == null) { + Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference."); return; } callback.onBackInvoked(); diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 43be0312245e8abfd52e00f661938a92d6f7a78e..1b901f5fd09c5d8a0d62fc88ea31723170c81fca 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets; +import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE; import static com.android.internal.util.ArrayUtils.convertToLongArray; import android.accessibilityservice.AccessibilityServiceInfo; @@ -147,11 +148,13 @@ public class AccessibilityShortcutController { Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, "1" /* Value to enable */, "0" /* Value to disable */, R.string.color_correction_feature_name)); - featuresMap.put(ONE_HANDED_COMPONENT_NAME, - new ToggleableFrameworkFeatureInfo( - Settings.Secure.ONE_HANDED_MODE_ACTIVATED, - "1" /* Value to enable */, "0" /* Value to disable */, - R.string.one_handed_mode_feature_name)); + if (SUPPORT_ONE_HANDED_MODE) { + featuresMap.put(ONE_HANDED_COMPONENT_NAME, + new ToggleableFrameworkFeatureInfo( + Settings.Secure.ONE_HANDED_MODE_ACTIVATED, + "1" /* Value to enable */, "0" /* Value to disable */, + R.string.one_handed_mode_feature_name)); + } featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME, new ToggleableFrameworkFeatureInfo( Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java index fc2c8cca9796bc2d53c55567e84038df16f1a135..2d87745fcadc7277c8194284561cbb54f75cd849 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -25,6 +25,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; +import static com.android.internal.os.RoSystemProperties.SUPPORT_ONE_HANDED_MODE; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityShortcutInfo; @@ -209,6 +210,7 @@ public final class AccessibilityTargetHelper { context.getString(R.string.accessibility_magnification_chooser_text), context.getDrawable(R.drawable.ic_accessibility_magnification), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + targets.add(magnification); final ToggleAllowListingFeatureTarget daltonizer = new ToggleAllowListingFeatureTarget(context, @@ -219,6 +221,7 @@ public final class AccessibilityTargetHelper { context.getString(R.string.color_correction_feature_name), context.getDrawable(R.drawable.ic_accessibility_color_correction), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); + targets.add(daltonizer); final ToggleAllowListingFeatureTarget colorInversion = new ToggleAllowListingFeatureTarget(context, @@ -229,16 +232,20 @@ public final class AccessibilityTargetHelper { context.getString(R.string.color_inversion_feature_name), context.getDrawable(R.drawable.ic_accessibility_color_inversion), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + targets.add(colorInversion); - final ToggleAllowListingFeatureTarget oneHandedMode = - new ToggleAllowListingFeatureTarget(context, - shortcutType, - isShortcutContained(context, shortcutType, - ONE_HANDED_COMPONENT_NAME.flattenToString()), - ONE_HANDED_COMPONENT_NAME.flattenToString(), - context.getString(R.string.one_handed_mode_feature_name), - context.getDrawable(R.drawable.ic_accessibility_one_handed), - Settings.Secure.ONE_HANDED_MODE_ACTIVATED); + if (SUPPORT_ONE_HANDED_MODE) { + final ToggleAllowListingFeatureTarget oneHandedMode = + new ToggleAllowListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, + ONE_HANDED_COMPONENT_NAME.flattenToString()), + ONE_HANDED_COMPONENT_NAME.flattenToString(), + context.getString(R.string.one_handed_mode_feature_name), + context.getDrawable(R.drawable.ic_accessibility_one_handed), + Settings.Secure.ONE_HANDED_MODE_ACTIVATED); + targets.add(oneHandedMode); + } final ToggleAllowListingFeatureTarget reduceBrightColors = new ToggleAllowListingFeatureTarget(context, @@ -249,11 +256,6 @@ public final class AccessibilityTargetHelper { context.getString(R.string.reduce_bright_colors_feature_name), context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors), Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED); - - targets.add(magnification); - targets.add(daltonizer); - targets.add(colorInversion); - targets.add(oneHandedMode); targets.add(reduceBrightColors); return targets; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 1fcfe7dd5b6fdb9e26962147382a896f159a9aa2..0a778a6538c34d8bbe97fe452048a9d3f149c0ac 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; +import static android.content.ContentProvider.getUserIdFromUri; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; @@ -161,6 +162,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1395,7 +1397,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1425,6 +1427,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } imagePreview.findViewById(R.id.content_preview_image_1_large) .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); @@ -1434,7 +1440,7 @@ public class ChooserActivity extends ResolverActivity implements List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1544,9 +1550,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1605,6 +1618,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); @@ -2953,11 +2984,23 @@ public class ChooserActivity extends ResolverActivity implements private boolean shouldShowStickyContentPreviewNoOrientationCheck() { return shouldShowTabs() - && mMultiProfilePagerAdapter.getListAdapterForUserHandle( - UserHandle.of(UserHandle.myUserId())).getCount() > 0 + && (mMultiProfilePagerAdapter.getListAdapterForUserHandle( + UserHandle.of(UserHandle.myUserId())).getCount() > 0 + || shouldShowContentPreviewWhenEmpty()) && shouldShowContentPreview(); } + /** + * This method could be used to override the default behavior when we hide the preview area + * when the current tab doesn't have any items. + * + * @return true if we want to show the content preview area even if the tab for the current + * user is empty + */ + protected boolean shouldShowContentPreviewWhenEmpty() { + return false; + } + /** * @return true if we want to show the content preview area */ diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java index e3cc4f12fcc670a34c627e5f53dfa72de2159886..d0b581158614de10219b30ccfa5db3a2ef7d797d 100644 --- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java +++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java @@ -47,7 +47,9 @@ public class ChooserActivityLoggerImpl implements ChooserActivityLogger { /* num_app_provided_app_targets = 6 */ appProvidedApp, /* is_workprofile = 7 */ isWorkprofile, /* previewType = 8 */ typeFromPreviewInt(previewType), - /* intentType = 9 */ typeFromIntentString(intent)); + /* intentType = 9 */ typeFromIntentString(intent), + /* num_provided_custom_actions = 10 */ 0, + /* reselection_action_provided = 11 */ false); } @Override diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index f8b764be582bc6226afbf828c71dc39ea45e137e..19e4ba405feb85ab06f256b35162515f59218df1 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -209,7 +209,7 @@ public class ResolverActivity extends Activity implements *

    Can only be used if there is a work profile. *

    Possible values can be either {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}. */ - static final String EXTRA_SELECTED_PROFILE = + protected static final String EXTRA_SELECTED_PROFILE = "com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE"; /** @@ -224,8 +224,8 @@ public class ResolverActivity extends Activity implements static final String EXTRA_CALLING_USER = "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER"; - static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL; - static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK; + protected static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL; + protected static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK; private BroadcastReceiver mWorkProfileStateReceiver; private UserHandle mHeaderCreatorUser; diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java index bce0d6076d24a79f72eca89892d791831c7c116c..f6bcc4661fd6e19577554a139119e91749e5037f 100644 --- a/core/java/com/android/internal/app/procstats/DumpUtils.java +++ b/core/java/com/android/internal/app/procstats/DumpUtils.java @@ -27,12 +27,12 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD; import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF; import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON; import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; -import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED; import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; import static com.android.internal.app.procstats.ProcessStats.STATE_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_FROZEN; import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; import static com.android.internal.app.procstats.ProcessStats.STATE_HOME; import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND; @@ -72,7 +72,8 @@ public final class DumpUtils { STATE_NAMES = new String[STATE_COUNT]; STATE_NAMES[STATE_PERSISTENT] = "Persist"; STATE_NAMES[STATE_TOP] = "Top"; - STATE_NAMES[STATE_BOUND_TOP_OR_FGS] = "BTopFgs"; + STATE_NAMES[STATE_BOUND_FGS] = "BFgs"; + STATE_NAMES[STATE_BOUND_TOP] = "BTop"; STATE_NAMES[STATE_FGS] = "Fgs"; STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg"; STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg"; @@ -83,14 +84,14 @@ public final class DumpUtils { STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt"; STATE_NAMES[STATE_HOME] = "Home"; STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct"; - STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct"; - STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct"; - STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty"; + STATE_NAMES[STATE_CACHED] = "Cached"; + STATE_NAMES[STATE_FROZEN] = "Frozen"; STATE_LABELS = new String[STATE_COUNT]; STATE_LABELS[STATE_PERSISTENT] = "Persistent"; STATE_LABELS[STATE_TOP] = " Top"; - STATE_LABELS[STATE_BOUND_TOP_OR_FGS] = "Bnd TopFgs"; + STATE_LABELS[STATE_BOUND_FGS] = " Bnd Fgs"; + STATE_LABELS[STATE_BOUND_TOP] = " Bnd Top"; STATE_LABELS[STATE_FGS] = " Fgs"; STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg"; STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg"; @@ -101,16 +102,16 @@ public final class DumpUtils { STATE_LABELS[STATE_HEAVY_WEIGHT] = " Heavy Wgt"; STATE_LABELS[STATE_HOME] = " (Home)"; STATE_LABELS[STATE_LAST_ACTIVITY] = "(Last Act)"; - STATE_LABELS[STATE_CACHED_ACTIVITY] = " (Cch Act)"; - STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT] = "(Cch CAct)"; - STATE_LABELS[STATE_CACHED_EMPTY] = "(Cch Emty)"; + STATE_LABELS[STATE_CACHED] = " (Cached)"; + STATE_LABELS[STATE_FROZEN] = " Frozen"; STATE_LABEL_CACHED = " (Cached)"; STATE_LABEL_TOTAL = " TOTAL"; STATE_NAMES_CSV = new String[STATE_COUNT]; STATE_NAMES_CSV[STATE_PERSISTENT] = "pers"; STATE_NAMES_CSV[STATE_TOP] = "top"; - STATE_NAMES_CSV[STATE_BOUND_TOP_OR_FGS] = "btopfgs"; + STATE_NAMES_CSV[STATE_BOUND_FGS] = "bfgs"; + STATE_NAMES_CSV[STATE_BOUND_TOP] = "btop"; STATE_NAMES_CSV[STATE_FGS] = "fgs"; STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg"; STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg"; @@ -121,14 +122,14 @@ public final class DumpUtils { STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy"; STATE_NAMES_CSV[STATE_HOME] = "home"; STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact"; - STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity"; - STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient"; - STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty"; + STATE_NAMES_CSV[STATE_CACHED] = "cached"; + STATE_NAMES_CSV[STATE_FROZEN] = "frzn"; STATE_TAGS = new String[STATE_COUNT]; STATE_TAGS[STATE_PERSISTENT] = "p"; STATE_TAGS[STATE_TOP] = "t"; - STATE_TAGS[STATE_BOUND_TOP_OR_FGS] = "d"; + STATE_TAGS[STATE_BOUND_FGS] = "y"; + STATE_TAGS[STATE_BOUND_TOP] = "z"; STATE_TAGS[STATE_FGS] = "g"; STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f"; STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b"; @@ -139,15 +140,14 @@ public final class DumpUtils { STATE_TAGS[STATE_HEAVY_WEIGHT] = "w"; STATE_TAGS[STATE_HOME] = "h"; STATE_TAGS[STATE_LAST_ACTIVITY] = "l"; - STATE_TAGS[STATE_CACHED_ACTIVITY] = "a"; - STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c"; - STATE_TAGS[STATE_CACHED_EMPTY] = "e"; + STATE_TAGS[STATE_CACHED] = "a"; + STATE_TAGS[STATE_FROZEN] = "e"; STATE_PROTO_ENUMS = new int[STATE_COUNT]; STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT; STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP; - STATE_PROTO_ENUMS[STATE_BOUND_TOP_OR_FGS] = - ProcessStatsEnums.PROCESS_STATE_BOUND_TOP_OR_FGS; + STATE_PROTO_ENUMS[STATE_BOUND_FGS] = ProcessStatsEnums.PROCESS_STATE_BOUND_FGS; + STATE_PROTO_ENUMS[STATE_BOUND_TOP] = ProcessStatsEnums.PROCESS_STATE_BOUND_TOP; STATE_PROTO_ENUMS[STATE_FGS] = ProcessStatsEnums.PROCESS_STATE_FGS; STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] = ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND; @@ -161,10 +161,8 @@ public final class DumpUtils { STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT; STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME; STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY; - STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY; - STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] = - ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; - STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY; + STATE_PROTO_ENUMS[STATE_CACHED] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY; + STATE_PROTO_ENUMS[STATE_FROZEN] = ProcessStatsEnums.PROCESS_STATE_FROZEN; // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data // aggregation / size reduction purposes. @@ -173,7 +171,9 @@ public final class DumpUtils { ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP_OR_FGS] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_FGS] = + ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS; + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BOUND_TOP] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FGS] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_FGS; @@ -196,11 +196,9 @@ public final class DumpUtils { ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] = - ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; - PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] = + PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_FROZEN] = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED; } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 72b9cd272d02667772ebeb7676bc4f3ecc28a792..fff778c616eeca10eca4ce9fd08b4ae2af93f23b 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -28,10 +28,9 @@ import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE; import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM; import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM; import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; -import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP_OR_FGS; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; -import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_FGS; +import static com.android.internal.app.procstats.ProcessStats.STATE_BOUND_TOP; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED; import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; import static com.android.internal.app.procstats.ProcessStats.STATE_FGS; import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; @@ -73,6 +72,7 @@ import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection; import java.io.PrintWriter; import java.util.Comparator; +import java.util.concurrent.TimeUnit; public final class ProcessState { private static final String TAG = "ProcessStats"; @@ -84,9 +84,9 @@ public final class ProcessState { STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI STATE_TOP, // ActivityManager.PROCESS_STATE_TOP - STATE_BOUND_TOP_OR_FGS, // ActivityManager.PROCESS_STATE_BOUND_TOP + STATE_BOUND_TOP, // ActivityManager.PROCESS_STATE_BOUND_TOP STATE_FGS, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE - STATE_BOUND_TOP_OR_FGS, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + STATE_BOUND_FGS, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND @@ -97,10 +97,10 @@ public final class ProcessState { STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT STATE_HOME, // ActivityManager.PROCESS_STATE_HOME STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY - STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_RECENT - STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_RECENT + STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_EMPTY }; public static final Comparator COMPARATOR = new Comparator() { @@ -925,8 +925,11 @@ public final class ProcessState { screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP], screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP_OR_FGS], - screenStates, memStates, new int[] { STATE_BOUND_TOP_OR_FGS}, now, totalTime, + dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_TOP], + screenStates, memStates, new int[] { STATE_BOUND_TOP }, now, totalTime, + true); + dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BOUND_FGS], + screenStates, memStates, new int[] { STATE_BOUND_FGS }, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_FGS], screenStates, memStates, new int[] { STATE_FGS}, now, totalTime, @@ -952,9 +955,6 @@ public final class ProcessState { screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true); dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY], screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); - dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED, - screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true); } public void dumpProcessState(PrintWriter pw, String prefix, @@ -1542,6 +1542,75 @@ public final class ProcessState { proto.write(fieldId, procName); } + /** Dumps the duration of each state to statsEventOutput. */ + public void dumpStateDurationToStatsd( + int atomTag, ProcessStats processStats, StatsEventOutput statsEventOutput) { + long topMs = 0; + long fgsMs = 0; + long boundTopMs = 0; + long boundFgsMs = 0; + long importantForegroundMs = 0; + long cachedMs = 0; + long frozenMs = 0; + long otherMs = 0; + for (int i = 0, size = mDurations.getKeyCount(); i < size; i++) { + final int key = mDurations.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + int procStateIndex = type % STATE_COUNT; + long duration = mDurations.getValue(key); + switch (procStateIndex) { + case STATE_TOP: + topMs += duration; + break; + case STATE_BOUND_FGS: + boundFgsMs += duration; + break; + case STATE_BOUND_TOP: + boundTopMs += duration; + break; + case STATE_FGS: + fgsMs += duration; + break; + case STATE_IMPORTANT_FOREGROUND: + case STATE_IMPORTANT_BACKGROUND: + importantForegroundMs += duration; + break; + case STATE_BACKUP: + case STATE_SERVICE: + case STATE_SERVICE_RESTARTING: + case STATE_RECEIVER: + case STATE_HEAVY_WEIGHT: + case STATE_HOME: + case STATE_LAST_ACTIVITY: + case STATE_PERSISTENT: + otherMs += duration; + break; + case STATE_CACHED: + cachedMs += duration; + break; + // TODO (b/261910877) Add support for tracking frozenMs. + } + } + statsEventOutput.write( + atomTag, + getUid(), + getName(), + (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodStartUptime), + (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodEndUptime), + (int) + TimeUnit.MILLISECONDS.toSeconds( + processStats.mTimePeriodEndUptime + - processStats.mTimePeriodStartUptime), + (int) TimeUnit.MILLISECONDS.toSeconds(topMs), + (int) TimeUnit.MILLISECONDS.toSeconds(fgsMs), + (int) TimeUnit.MILLISECONDS.toSeconds(boundTopMs), + (int) TimeUnit.MILLISECONDS.toSeconds(boundFgsMs), + (int) TimeUnit.MILLISECONDS.toSeconds(importantForegroundMs), + (int) TimeUnit.MILLISECONDS.toSeconds(cachedMs), + (int) TimeUnit.MILLISECONDS.toSeconds(frozenMs), + (int) TimeUnit.MILLISECONDS.toSeconds(otherMs)); + } + /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */ public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId, String procName, int uid, long now, diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index d2b2f0a2b8947426784ae04e7d48bd5d385f2666..3ce234b4167b305884bb9bb3fb038260563681de 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -43,6 +43,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.AssociationState.SourceKey; import com.android.internal.app.procstats.AssociationState.SourceState; +import com.android.internal.util.function.QuintConsumer; import dalvik.system.VMRuntime; @@ -56,6 +57,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -78,21 +81,21 @@ public final class ProcessStats implements Parcelable { public static final int STATE_NOTHING = -1; public static final int STATE_PERSISTENT = 0; public static final int STATE_TOP = 1; - public static final int STATE_BOUND_TOP_OR_FGS = 2; + public static final int STATE_BOUND_TOP = 2; public static final int STATE_FGS = 3; - public static final int STATE_IMPORTANT_FOREGROUND = 4; - public static final int STATE_IMPORTANT_BACKGROUND = 5; - public static final int STATE_BACKUP = 6; - public static final int STATE_SERVICE = 7; - public static final int STATE_SERVICE_RESTARTING = 8; - public static final int STATE_RECEIVER = 9; - public static final int STATE_HEAVY_WEIGHT = 10; - public static final int STATE_HOME = 11; - public static final int STATE_LAST_ACTIVITY = 12; - public static final int STATE_CACHED_ACTIVITY = 13; - public static final int STATE_CACHED_ACTIVITY_CLIENT = 14; - public static final int STATE_CACHED_EMPTY = 15; - public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + public static final int STATE_BOUND_FGS = 4; + public static final int STATE_IMPORTANT_FOREGROUND = 5; + public static final int STATE_IMPORTANT_BACKGROUND = 6; + public static final int STATE_BACKUP = 7; + public static final int STATE_SERVICE = 8; + public static final int STATE_SERVICE_RESTARTING = 9; + public static final int STATE_RECEIVER = 10; + public static final int STATE_HEAVY_WEIGHT = 11; + public static final int STATE_HOME = 12; + public static final int STATE_LAST_ACTIVITY = 13; + public static final int STATE_CACHED = 14; + public static final int STATE_FROZEN = 15; + public static final int STATE_COUNT = STATE_FROZEN + 1; public static final int PSS_SAMPLE_COUNT = 0; public static final int PSS_MINIMUM = 1; @@ -151,9 +154,10 @@ public final class ProcessStats implements Parcelable { public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; public static final int[] NON_CACHED_PROC_STATES = new int[] { - STATE_PERSISTENT, STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS, + STATE_PERSISTENT, STATE_TOP, STATE_FGS, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, - STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT + STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT, + STATE_BOUND_TOP, STATE_BOUND_FGS }; public static final int[] BACKGROUND_PROC_STATES = new int[] { @@ -162,11 +166,11 @@ public final class ProcessStats implements Parcelable { }; public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, - STATE_TOP, STATE_BOUND_TOP_OR_FGS, STATE_FGS, STATE_IMPORTANT_FOREGROUND, + STATE_TOP, STATE_FGS, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, - STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY + STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED, + STATE_BOUND_TOP, STATE_BOUND_FGS, STATE_FROZEN }; // Should report process stats. @@ -2389,6 +2393,79 @@ public final class ProcessStats implements Parcelable { } } + void forEachProcess(Consumer consumer) { + final ArrayMap> procMap = mProcesses.getMap(); + for (int ip = 0, size = procMap.size(); ip < size; ip++) { + final SparseArray uids = procMap.valueAt(ip); + for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) { + final ProcessState processState = uids.valueAt(iu); + consumer.accept(processState); + } + } + } + + void forEachAssociation( + QuintConsumer consumer) { + final ArrayMap>> pkgMap = + mPackages.getMap(); + for (int ip = 0, size = pkgMap.size(); ip < size; ip++) { + final SparseArray> uids = pkgMap.valueAt(ip); + for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) { + final int uid = uids.keyAt(iu); + final LongSparseArray versions = uids.valueAt(iu); + for (int iv = 0, versionsSize = versions.size(); iv < versionsSize; iv++) { + final PackageState state = versions.valueAt(iv); + for (int iasc = 0, ascSize = state.mAssociations.size(); + iasc < ascSize; + iasc++) { + final String serviceName = state.mAssociations.keyAt(iasc); + final AssociationState asc = state.mAssociations.valueAt(iasc); + for (int is = 0, sourcesSize = asc.mSources.size(); + is < sourcesSize; + is++) { + final SourceState src = asc.mSources.valueAt(is); + final SourceKey key = asc.mSources.keyAt(is); + consumer.accept(asc, uid, serviceName, key, src); + } + } + } + } + } + } + + /** Dumps the stats of all processes to statsEventOutput. */ + public void dumpProcessState(int atomTag, StatsEventOutput statsEventOutput) { + forEachProcess( + (processState) -> { + if (processState.isMultiPackage() + && processState.getCommonProcess() != processState) { + return; + } + processState.dumpStateDurationToStatsd(atomTag, this, statsEventOutput); + }); + } + + /** Dumps all process association data to statsEventOutput. */ + public void dumpProcessAssociation(int atomTag, StatsEventOutput statsEventOutput) { + forEachAssociation( + (asc, serviceUid, serviceName, key, src) -> { + statsEventOutput.write( + atomTag, + key.mUid, + key.mProcess, + serviceUid, + serviceName, + (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodStartUptime), + (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodEndUptime), + (int) + TimeUnit.MILLISECONDS.toSeconds( + mTimePeriodEndUptime - mTimePeriodStartUptime), + (int) TimeUnit.MILLISECONDS.toSeconds(src.mDuration), + src.mActiveCount, + asc.getProcessName()); + }); + } + private void dumpProtoPreamble(ProtoOutputStream proto) { proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime); proto.write(ProcessStatsSectionProto.END_REALTIME_MS, diff --git a/core/java/com/android/internal/app/procstats/StatsEventOutput.java b/core/java/com/android/internal/app/procstats/StatsEventOutput.java new file mode 100644 index 0000000000000000000000000000000000000000..b2e405475a4e4a3fa56478a553c212bd7a04e5fa --- /dev/null +++ b/core/java/com/android/internal/app/procstats/StatsEventOutput.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app.procstats; + +import android.util.StatsEvent; + +import com.android.internal.util.FrameworkStatsLog; + +import java.util.List; + +/** + * A simple wrapper of FrameworkStatsLog.buildStatsEvent. This allows unit tests to mock out the + * dependency. + */ +public class StatsEventOutput { + + List mOutput; + + public StatsEventOutput(List output) { + mOutput = output; + } + + /** Writes the data to the output. */ + public void write( + int atomTag, + int uid, + String processName, + int measurementStartUptimeSecs, + int measurementEndUptimeSecs, + int measurementDurationUptimeSecs, + int topSeconds, + int fgsSeconds, + int boundTopSeconds, + int boundFgsSeconds, + int importantForegroundSeconds, + int cachedSeconds, + int frozenSeconds, + int otherSeconds) { + mOutput.add( + FrameworkStatsLog.buildStatsEvent( + atomTag, + uid, + processName, + measurementStartUptimeSecs, + measurementEndUptimeSecs, + measurementDurationUptimeSecs, + topSeconds, + fgsSeconds, + boundTopSeconds, + boundFgsSeconds, + importantForegroundSeconds, + cachedSeconds, + frozenSeconds, + otherSeconds)); + } + + /** Writes the data to the output. */ + public void write( + int atomTag, + int clientUid, + String processName, + int serviceUid, + String serviceName, + int measurementStartUptimeSecs, + int measurementEndUptimeSecs, + int measurementDurationUptimeSecs, + int activeDurationUptimeSecs, + int activeCount, + String serviceProcessName) { + mOutput.add( + FrameworkStatsLog.buildStatsEvent( + atomTag, + clientUid, + processName, + serviceUid, + serviceName, + measurementStartUptimeSecs, + measurementEndUptimeSecs, + measurementDurationUptimeSecs, + activeDurationUptimeSecs, + activeCount, + serviceProcessName)); + } +} diff --git a/core/java/com/android/internal/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java index 8761b7470cd33aceff67910bfe9397201adbb645..49113465c26afcf5e1d42a8c37996a831cd821c2 100644 --- a/core/java/com/android/internal/app/procstats/UidState.java +++ b/core/java/com/android/internal/app/procstats/UidState.java @@ -150,6 +150,7 @@ public final class UidState { public void resetSafely(long now) { mDurations.resetTable(); mStartTime = now; + mProcesses.removeIf(p -> !p.isInUse()); } /** diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index b916878ff461274af0b1ae2dcc494d0b47ef399a..3303c0e73e076538275da685fdcd14f5ccd83c67 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -562,9 +562,9 @@ public final class SystemUiDeviceConfigFlags { "task_manager_show_user_visible_jobs"; /** - * (boolean) Whether the clipboard overlay is enabled. + * (boolean) Whether to show notification volume control slider separate from ring. */ - public static final String CLIPBOARD_OVERLAY_ENABLED = "clipboard_overlay_enabled"; + public static final String VOLUME_SEPARATE_NOTIFICATION = "volume_separate_notification"; /** * (boolean) Whether widget provider info would be saved to / loaded from system persistence @@ -572,13 +572,6 @@ public final class SystemUiDeviceConfigFlags { */ public static final String PERSISTS_WIDGET_PROVIDER_INFO = "persists_widget_provider_info"; - /** - * (boolean) Whether the clipboard overlay shows an edit button (as opposed to requiring tapping - * the preview to send an edit intent). - */ - public static final String CLIPBOARD_OVERLAY_SHOW_EDIT_BUTTON = - "clipboard_overlay_show_edit_button"; - /** * (boolean) Whether to show smart chips (based on TextClassifier) in the clipboard overlay. */ diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java new file mode 100644 index 0000000000000000000000000000000000000000..c946db1dbda97fdf5493495f8ab2664aac595ada --- /dev/null +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -0,0 +1,188 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.config.sysui; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Provides a central definition of debug SystemUI's SystemProperties flags, and their defaults. + * + * The main feature of this class is that it encodes a system-wide default for each flag which can + * be updated by engineers with a single-line CL. + * + * NOTE: Because flag values returned by this class are not cached, it is important that developers + * understand the intricacies of changing values and how that applies to their own code. + * Generally, the best practice is to set the property, and then restart the device so that any + * processes with stale state can be updated. However, if your code has no state derived from the + * flag value and queries it any time behavior is relevant, then it may be safe to change the flag + * and not immediately reboot. + * + * To enable flags in debuggable builds, use the following commands: + * + * $ adb shell setprop persist.sysui.whatever_the_flag true + * $ adb reboot + * + * @hide + */ +public class SystemUiSystemPropertiesFlags { + + /** The teamfood flag allows multiple features to be opted into at once. */ + public static final Flag TEAMFOOD = devFlag("persist.sysui.teamfood"); + + /** + * Flags related to notification features + */ + public static final class NotificationFlags { + + /** + * FOR DEVELOPMENT / TESTING ONLY!!! + * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission. + * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI + */ + public static final Flag FSI_FORCE_DEMOTE = + devFlag("persist.sysui.notification.fsi_force_demote"); + + /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */ + public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI = + devFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi"); + + /** Gating the ability for users to dismiss ongoing event notifications */ + public static final Flag ALLOW_DISMISS_ONGOING = + devFlag("persist.sysui.notification.ongoing_dismissal"); + + /** Gating the redaction of OTP notifications on the lockscreen */ + public static final Flag OTP_REDACTION = + devFlag("persist.sysui.notification.otp_redaction"); + + } + + //// == End of flags. Everything below this line is the implementation. == //// + + /** The interface used for resolving SystemUI SystemProperties Flags to booleans. */ + public interface FlagResolver { + /** Is the flag enabled? */ + boolean isEnabled(Flag flag); + } + + /** The primary, immutable resolver returned by getResolver() */ + private static final FlagResolver + MAIN_RESOLVER = + Build.IS_DEBUGGABLE ? new DebugResolver() : new ProdResolver(); + + /** + * On debuggable builds, this can be set to override the resolver returned by getResolver(). + * This can be useful to override flags when testing components that do not allow injecting the + * SystemUiPropertiesFlags resolver they use. + * Always set this to null when tests tear down. + */ + @VisibleForTesting + public static FlagResolver TEST_RESOLVER = null; + + /** Get the resolver for this device configuration. */ + public static FlagResolver getResolver() { + if (Build.IS_DEBUGGABLE && TEST_RESOLVER != null) { + Log.i("SystemUiSystemPropertiesFlags", "Returning debug resolver " + TEST_RESOLVER); + return TEST_RESOLVER; + } + return MAIN_RESOLVER; + } + + /** + * Creates a flag that is enabled by default in debuggable builds. + * It can be enabled by setting this flag's SystemProperty to 1. + * + * This flag is ALWAYS disabled in release builds. + */ + @VisibleForTesting + public static Flag devFlag(String name) { + return new Flag(name, false, null); + } + + /** + * Creates a flag that is disabled by default in debuggable builds. + * It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0. + * If this flag's SystemProperty is not set, the flag can be enabled by setting the + * TEAMFOOD flag's SystemProperty to 1. + * + * This flag is ALWAYS disabled in release builds. + */ + @VisibleForTesting + public static Flag teamfoodFlag(String name) { + return new Flag(name, false, TEAMFOOD); + } + + /** + * Creates a flag that is enabled by default in debuggable builds. + * It can be enabled by setting this flag's SystemProperty to 0. + * + * This flag is ALWAYS enabled in release builds. + */ + @VisibleForTesting + public static Flag releasedFlag(String name) { + return new Flag(name, true, null); + } + + /** Represents a developer-switchable gate for a feature. */ + public static final class Flag { + public final String mSysPropKey; + public final boolean mDefaultValue; + @Nullable + public final Flag mDebugDefault; + + /** constructs a new flag. only visible for testing the class */ + @VisibleForTesting + public Flag(@NonNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault) { + mSysPropKey = sysPropKey; + mDefaultValue = defaultValue; + mDebugDefault = debugDefault; + } + } + + /** Implementation of the interface used in release builds. */ + @VisibleForTesting + public static final class ProdResolver implements + FlagResolver { + @Override + public boolean isEnabled(Flag flag) { + return flag.mDefaultValue; + } + } + + /** Implementation of the interface used in debuggable builds. */ + @VisibleForTesting + public static class DebugResolver implements FlagResolver { + @Override + public final boolean isEnabled(Flag flag) { + if (flag.mDebugDefault == null) { + return getBoolean(flag.mSysPropKey, flag.mDefaultValue); + } + return getBoolean(flag.mSysPropKey, isEnabled(flag.mDebugDefault)); + } + + /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ + @VisibleForTesting + public boolean getBoolean(String key, boolean defaultValue) { + return SystemProperties.getBoolean(key, defaultValue); + } + } +} diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index d8afe50d3af3f12e8f8fd4ed352c421050e90037..475f7fd2bdae1c22221fb3185b056dbeb649155b 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -16,6 +16,10 @@ package com.android.internal.jank; +import static android.Manifest.permission.READ_DEVICE_CONFIG; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.provider.DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR; + import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL; import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT; import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; @@ -33,6 +37,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; @@ -93,6 +98,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UiThread; import android.annotation.WorkerThread; +import android.app.ActivityThread; import android.content.Context; import android.os.Build; import android.os.Handler; @@ -231,6 +237,7 @@ public class InteractionJankMonitor { public static final int CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS = 66; public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67; public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68; + public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; private static final int NO_STATSD_LOGGING = -1; @@ -308,6 +315,8 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_SWIPE_TO_RECENTS, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME, + NO_STATSD_LOGGING, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION, }; private static volatile InteractionJankMonitor sInstance; @@ -396,7 +405,8 @@ public class InteractionJankMonitor { CUJ_RECENTS_SCROLLING, CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS, CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE, - CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME + CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME, + CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -431,18 +441,37 @@ public class InteractionJankMonitor { mWorker = worker; mWorker.start(); mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; + mEnabled = DEFAULT_ENABLED; + + final Context context = ActivityThread.currentApplication(); + if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) { + if (DEBUG) { + Log.d(TAG, "Initialized the InteractionJankMonitor." + + " (No READ_DEVICE_CONFIG permission to change configs)" + + " enabled=" + mEnabled + ", interval=" + mSamplingInterval + + ", missedFrameThreshold=" + mTraceThresholdMissedFrames + + ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis + + ", package=" + context.getPackageName()); + } + return; + } - // Post initialization to the background in case we're running on the main - // thread. + // Post initialization to the background in case we're running on the main thread. mWorker.getThreadHandler().post( - () -> mPropertiesChangedListener.onPropertiesChanged( - DeviceConfig.getProperties( - DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR))); - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, - new HandlerExecutor(mWorker.getThreadHandler()), - mPropertiesChangedListener); - mEnabled = DEFAULT_ENABLED; + () -> { + try { + mPropertiesChangedListener.onPropertiesChanged( + DeviceConfig.getProperties(NAMESPACE_INTERACTION_JANK_MONITOR)); + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_INTERACTION_JANK_MONITOR, + new HandlerExecutor(mWorker.getThreadHandler()), + mPropertiesChangedListener); + } catch (SecurityException ex) { + Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted=" + + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) + + ", package=" + context.getPackageName()); + } + }); } /** @@ -917,6 +946,8 @@ public class InteractionJankMonitor { return "LAUNCHER_CLOSE_ALL_APPS_SWIPE"; case CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME: return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME"; + case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION: + return "LOCKSCREEN_CLOCK_MOVE_ANIMATION"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 05c6842784278bed71c7f5f902c102e765318d30..852cfe32cc902cc899ea3385fe28fa6a5d0eb730 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -167,7 +167,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version. Must be updated when the format of the parcelable changes - public static final int VERSION = 210; + public static final int VERSION = 211; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -6514,6 +6514,9 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mPhoneOn = true; mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs); + if (mConstants.PHONE_ON_EXTERNAL_STATS_COLLECTION) { + scheduleSyncExternalStatsLocked("phone-on", ExternalStatsSync.UPDATE_RADIO); + } } } @@ -6532,6 +6535,7 @@ public class BatteryStatsImpl extends BatteryStats { addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mPhoneOn = false; mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs); + scheduleSyncExternalStatsLocked("phone-off", ExternalStatsSync.UPDATE_RADIO); } } @@ -8507,6 +8511,12 @@ public class BatteryStatsImpl extends BatteryStats { return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO); } + @GuardedBy("this") + @Override + public long getPhoneEnergyConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_PHONE); + } + @GuardedBy("this") @Override public long getScreenOnMeasuredBatteryConsumptionUC() { @@ -13751,18 +13761,36 @@ public class BatteryStatsImpl extends BatteryStats { } synchronized (this) { + final long totalRadioDurationMs = + mMobileRadioActiveTimer.getTimeSinceMarkLocked( + elapsedRealtimeMs * 1000) / 1000; + mMobileRadioActiveTimer.setMark(elapsedRealtimeMs); + final long phoneOnDurationMs = Math.min(totalRadioDurationMs, + mPhoneOnTimer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000); + mPhoneOnTimer.setMark(elapsedRealtimeMs); + if (!mOnBatteryInternal || mIgnoreNextExternalStats) { return; } final SparseDoubleArray uidEstimatedConsumptionMah; + final long dataConsumedChargeUC; if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null && mGlobalMeasuredEnergyStats != null) { + // Crudely attribute power consumption. Added (totalRadioDurationMs / 2) to the + // numerator for long rounding. + final long phoneConsumedChargeUC = + (consumedChargeUC * phoneOnDurationMs + totalRadioDurationMs / 2) + / totalRadioDurationMs; + dataConsumedChargeUC = consumedChargeUC - phoneConsumedChargeUC; mGlobalMeasuredEnergyStats.updateStandardBucket( - MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC); + MeasuredEnergyStats.POWER_BUCKET_PHONE, phoneConsumedChargeUC); + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, dataConsumedChargeUC); uidEstimatedConsumptionMah = new SparseDoubleArray(); } else { uidEstimatedConsumptionMah = null; + dataConsumedChargeUC = POWER_DATA_UNAVAILABLE; } if (deltaInfo != null) { @@ -13922,14 +13950,9 @@ public class BatteryStatsImpl extends BatteryStats { // Update the MeasuredEnergyStats information. if (uidEstimatedConsumptionMah != null) { double totalEstimatedConsumptionMah = 0.0; - - // Estimate total active radio power consumption since last mark. - final long totalRadioTimeMs = mMobileRadioActiveTimer.getTimeSinceMarkLocked( - elapsedRealtimeMs * 1000) / 1000; - mMobileRadioActiveTimer.setMark(elapsedRealtimeMs); totalEstimatedConsumptionMah += mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah( - totalRadioTimeMs); + totalRadioDurationMs); // Estimate idle power consumption at each signal strength level final int numSignalStrengthLevels = mPhoneSignalStrengthsTimer.length; @@ -13953,7 +13976,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs); distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, - consumedChargeUC, uidEstimatedConsumptionMah, + dataConsumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah, elapsedRealtimeMs); } @@ -16685,6 +16708,8 @@ public class BatteryStatsImpl extends BatteryStats { public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb"; public static final String KEY_BATTERY_CHARGED_DELAY_MS = "battery_charged_delay_ms"; + public static final String KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION = + "phone_on_external_stats_collection"; private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true; private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000; @@ -16697,6 +16722,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64; private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/ private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */ + private static final boolean DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION = true; public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME; /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an @@ -16712,6 +16738,8 @@ public class BatteryStatsImpl extends BatteryStats { public int MAX_HISTORY_FILES; public int MAX_HISTORY_BUFFER; /*Bytes*/ public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS; + public boolean PHONE_ON_EXTERNAL_STATS_COLLECTION = + DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -16788,6 +16816,11 @@ public class BatteryStatsImpl extends BatteryStats { DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB : DEFAULT_MAX_HISTORY_BUFFER_KB) * 1024; + + PHONE_ON_EXTERNAL_STATS_COLLECTION = mParser.getBoolean( + KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION, + DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION); + updateBatteryChargedDelayMsLocked(); } } @@ -16842,6 +16875,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(MAX_HISTORY_BUFFER/1024); pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("="); pw.println(BATTERY_CHARGED_DELAY_MS); + pw.print(KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION); pw.print("="); + pw.println(PHONE_ON_EXTERNAL_STATS_COLLECTION); } } diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 09e409bd934ef6bf3a58af8a85e9a7f489b26247..ac4976f2a46a4af815ce63986df2acd02286292d 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -298,18 +298,16 @@ public class BatteryUsageStatsProvider { BatteryStats.Uid.PROCESS_STATE_FOREGROUND, realtimeUs, BatteryStats.STATS_SINCE_CHARGED); - totalForegroundDurationUs += uid.getProcessStateTime( - BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, realtimeUs, - BatteryStats.STATS_SINCE_CHARGED); - return totalForegroundDurationUs / 1000; } private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { - return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs, - BatteryStats.STATS_SINCE_CHARGED) / 1000; + return (uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, + realtimeUs, BatteryStats.STATS_SINCE_CHARGED) + + uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, + realtimeUs, BatteryStats.STATS_SINCE_CHARGED)) + / 1000; } - private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) { final boolean includePowerModels = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index cb893defab14238611223db79322e6533661d484..f1c4ffe077883967dd30ba5568406c4c5bb5344c 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -40,14 +40,27 @@ public class PhonePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long energyConsumerUC = batteryStats.getPhoneEnergyConsumptionUC(); + final int powerModel = getPowerModel(energyConsumerUC, query); + final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); - if (phoneOnPower != 0) { - builder.getAggregateBatteryConsumerBuilder( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower) - .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs); + final double phoneOnPower; + switch (powerModel) { + case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY: + phoneOnPower = uCtoMah(energyConsumerUC); + break; + case BatteryConsumer.POWER_MODEL_POWER_PROFILE: + default: + phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); } + + if (phoneOnPower == 0.0) return; + + builder.getAggregateBatteryConsumerBuilder( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower, powerModel) + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs); + } } diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java index 98d81c9598b8fa8c92dedebfa53d22b7c6aed4c8..cccd80e824209019c4a022e27ac8e722a0f07503 100644 --- a/core/java/com/android/internal/os/RoSystemProperties.java +++ b/core/java/com/android/internal/os/RoSystemProperties.java @@ -31,6 +31,8 @@ public class RoSystemProperties { SystemProperties.getInt("ro.factorytest", 0); public static final String CONTROL_PRIVAPP_PERMISSIONS = SystemProperties.get("ro.control_privapp_permissions"); + public static final boolean SUPPORT_ONE_HANDED_MODE = + SystemProperties.getBoolean("ro.support_one_handed_mode", /* def= */ false); // ------ ro.hdmi.* -------- // /** diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java index 205c5fd735eac795225373acb63da57f4672dc6a..f1ed3bed5d8921d808b0908cbba421335f478349 100644 --- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java +++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java @@ -56,6 +56,9 @@ public class GestureNavigationSettingsObserver extends ContentObserver { } }; + /** + * Registers the observer for all users. + */ public void register() { ContentResolver r = mContext.getContentResolver(); r.registerContentObserver( @@ -73,6 +76,26 @@ public class GestureNavigationSettingsObserver extends ContentObserver { mOnPropertiesChangedListener); } + /** + * Registers the observer for the calling user. + */ + public void registerForCallingUser() { + ContentResolver r = mContext.getContentResolver(); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT), + false, this); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT), + false, this); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), + false, this); + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_SYSTEMUI, + runnable -> mMainHandler.post(runnable), + mOnPropertiesChangedListener); + } + public void unregister() { mContext.getContentResolver().unregisterContentObserver(this); DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); @@ -86,12 +109,46 @@ public class GestureNavigationSettingsObserver extends ContentObserver { } } + /** + * Returns the left sensitivity for the current user. To be used in code that runs primarily + * in one user's process. + */ public int getLeftSensitivity(Resources userRes) { - return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT); + final float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f, UserHandle.USER_CURRENT); + return (int) (getUnscaledInset(userRes) * scale); } + /** + * Returns the left sensitivity for the calling user. To be used in code that runs in a + * per-user process. + */ + @SuppressWarnings("NonUserGetterCalled") + public int getLeftSensitivityForCallingUser(Resources userRes) { + final float scale = Settings.Secure.getFloat(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f); + return (int) (getUnscaledInset(userRes) * scale); + } + + /** + * Returns the right sensitivity for the current user. To be used in code that runs primarily + * in one user's process. + */ public int getRightSensitivity(Resources userRes) { - return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT); + final float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f, UserHandle.USER_CURRENT); + return (int) (getUnscaledInset(userRes) * scale); + } + + /** + * Returns the right sensitivity for the calling user. To be used in code that runs in a + * per-user process. + */ + @SuppressWarnings("NonUserGetterCalled") + public int getRightSensitivityForCallingUser(Resources userRes) { + final float scale = Settings.Secure.getFloat(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f); + return (int) (getUnscaledInset(userRes) * scale); } public boolean areNavigationButtonForcedVisible() { @@ -99,7 +156,7 @@ public class GestureNavigationSettingsObserver extends ContentObserver { Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0; } - private int getSensitivity(Resources userRes, String side) { + private float getUnscaledInset(Resources userRes) { final DisplayMetrics dm = userRes.getDisplayMetrics(); final float defaultInset = userRes.getDimension( com.android.internal.R.dimen.config_backGestureInset) / dm.density; @@ -110,8 +167,6 @@ public class GestureNavigationSettingsObserver extends ContentObserver { : defaultInset; final float inset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, backGestureInset, dm); - final float scale = Settings.Secure.getFloatForUser( - mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT); - return (int) (inset * scale); + return inset; } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index fb38bba8ee16700438350c306210de9a95ac690f..e603e2ed57f1aef4c648562e595fc7473134e059 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -295,6 +295,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mClosingActionMenu; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; + private int mAudioMode = AudioManager.MODE_NORMAL; private MediaController mMediaController; private AudioManager mAudioManager; @@ -317,6 +318,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } }; + private AudioManager.OnModeChangedListener mOnModeChangedListener; + private Transition mEnterTransition = null; private Transition mReturnTransition = USE_DEFAULT_TRANSITION; private Transition mExitTransition = null; @@ -379,8 +382,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // window, as we'll be skipping the addView in handleResumeActivity(), and // the token will not be updated as for a new window. getAttributes().token = preservedWindow.getAttributes().token; - mProxyOnBackInvokedDispatcher.setActualDispatcher( - preservedWindow.getOnBackInvokedDispatcher()); + final ViewRootImpl viewRoot = mDecor.getViewRootImpl(); + if (viewRoot != null) { + // Clear the old callbacks and attach to the new window. + viewRoot.getOnBackInvokedDispatcher().clear(); + onViewRootImplSet(viewRoot); + } } // Even though the device doesn't support picture-in-picture mode, // an user can force using it through developer options. @@ -1946,9 +1953,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { - // If we have a session send it the volume command, otherwise - // use the suggested stream. - if (mMediaController != null) { + // If we have a session and no active phone call send it the volume command, + // otherwise use the suggested stream. + if (mMediaController != null && !isActivePhoneCallOngoing()) { getMediaSessionManager().dispatchVolumeKeyEventToSessionAsSystemService(event, mMediaController.getSessionToken()); } else { @@ -1999,6 +2006,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } + private boolean isActivePhoneCallOngoing() { + return mAudioMode == AudioManager.MODE_IN_CALL + || mAudioMode == AudioManager.MODE_IN_COMMUNICATION; + } + private KeyguardManager getKeyguardManager() { if (mKeyguardManager == null) { mKeyguardManager = (KeyguardManager) getContext().getSystemService( @@ -2322,6 +2334,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + @Override + protected void onDestroy() { + if (mOnModeChangedListener != null) { + getAudioManager().removeOnModeChangedListener(mOnModeChangedListener); + mOnModeChangedListener = null; + } + } + private class PanelMenuPresenterCallback implements MenuPresenter.Callback { @Override public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { @@ -3204,6 +3224,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setMediaController(MediaController controller) { mMediaController = controller; + if (controller != null && mOnModeChangedListener == null) { + mAudioMode = getAudioManager().getMode(); + mOnModeChangedListener = mode -> mAudioMode = mode; + getAudioManager().addOnModeChangedListener(getContext().getMainExecutor(), + mOnModeChangedListener); + } else if (mOnModeChangedListener != null) { + getAudioManager().removeOnModeChangedListener(mOnModeChangedListener); + mOnModeChangedListener = null; + } } @Override diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index 7fb8696a217d2f58fd0248d28f2781e2d97f7ee2..5bfdd62592c8b957a5c445274438cedc4eab11fe 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -59,7 +59,9 @@ public class MeasuredEnergyStats { public static final int POWER_BUCKET_BLUETOOTH = 5; public static final int POWER_BUCKET_GNSS = 6; public static final int POWER_BUCKET_MOBILE_RADIO = 7; - public static final int NUMBER_STANDARD_POWER_BUCKETS = 8; // Buckets above this are custom. + public static final int POWER_BUCKET_CAMERA = 8; + public static final int POWER_BUCKET_PHONE = 9; + public static final int NUMBER_STANDARD_POWER_BUCKETS = 10; // Buckets above this are custom. @IntDef(prefix = {"POWER_BUCKET_"}, value = { POWER_BUCKET_UNKNOWN, diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 017bf3f3dd6c5b3b448509138067f2a5b8ca6326..04fc4a6fd81dea3f1e1c81839ddbfc1911edeb99 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -338,4 +338,11 @@ oneway interface IStatusBar * @param leftOrTop indicates where the stage split is. */ void enterStageSplitFromRunningApp(boolean leftOrTop); + + /** + * Shows the media output switcher dialog. + * + * @param packageName of the session for which the output switcher is shown. + */ + void showMediaOutputSwitcher(String packageName); } diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index afb526aeea6fb2843cbe9a320c6e2877787da078..c8a8b586590b2e507fa79a04178084b6c05e4f01 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -24,12 +24,15 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS; @@ -187,6 +190,25 @@ public class LatencyTracker { */ public static final int ACTION_SHOW_VOICE_INTERACTION = 19; + /** + * Time it takes to request IME shown animation. + */ + public static final int ACTION_REQUEST_IME_SHOWN = 20; + + /** + * Time it takes to request IME hidden animation. + */ + public static final int ACTION_REQUEST_IME_HIDDEN = 21; + + /** + * Time it takes to load the animation frames in smart space doorbell card. + * It measures the duration from the images uris are passed into the view + * to all the frames are loaded. + *

    + * A long latency makes the doorbell animation looks janky until all the frames are loaded. + */ + public static final int ACTION_SMARTSPACE_DOORBELL = 22; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -208,6 +230,9 @@ public class LatencyTracker { ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, ACTION_SHOW_VOICE_INTERACTION, + ACTION_REQUEST_IME_SHOWN, + ACTION_REQUEST_IME_HIDDEN, + ACTION_SMARTSPACE_DOORBELL, }; /** @hide */ @@ -232,6 +257,9 @@ public class LatencyTracker { ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, ACTION_SHOW_VOICE_INTERACTION, + ACTION_REQUEST_IME_SHOWN, + ACTION_REQUEST_IME_HIDDEN, + ACTION_SMARTSPACE_DOORBELL, }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -259,6 +287,9 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR, UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD, UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL, }; private static LatencyTracker sLatencyTracker; @@ -368,6 +399,12 @@ public class LatencyTracker { return "ACTION_FOLD_TO_AOD"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION: return "ACTION_SHOW_VOICE_INTERACTION"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN: + return "ACTION_REQUEST_IME_SHOWN"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN: + return "ACTION_REQUEST_IME_HIDDEN"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL: + return "ACTION_SMARTSPACE_DOORBELL"; default: throw new IllegalArgumentException("Invalid action"); } @@ -434,7 +471,7 @@ public class LatencyTracker { */ public void onActionStart(@Action int action, String tag) { synchronized (mLock) { - if (!isEnabled()) { + if (!isEnabled(action)) { return; } // skip if the action is already instrumenting. @@ -458,7 +495,7 @@ public class LatencyTracker { */ public void onActionEnd(@Action int action) { synchronized (mLock) { - if (!isEnabled()) { + if (!isEnabled(action)) { return; } Session session = mSessions.get(action); @@ -568,23 +605,27 @@ public class LatencyTracker { void begin(@NonNull Runnable timeoutAction) { mStartRtc = SystemClock.elapsedRealtime(); - Trace.asyncTraceBegin(TRACE_TAG_APP, traceName(), 0); + Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, traceName(), traceName(), 0); // start counting timeout. - mTimeoutRunnable = timeoutAction; + mTimeoutRunnable = () -> { + Trace.instantForTrack(TRACE_TAG_APP, traceName(), "timeout"); + timeoutAction.run(); + }; BackgroundThread.getHandler() .postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(15)); } void end() { mEndRtc = SystemClock.elapsedRealtime(); - Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0); + Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), "end", 0); BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable); mTimeoutRunnable = null; } void cancel() { - Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0); + Trace.instantForTrack(TRACE_TAG_APP, traceName(), "cancel"); + Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, traceName(), "cancel", 0); BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable); mTimeoutRunnable = null; } diff --git a/core/java/com/android/internal/util/ObservableServiceConnection.java b/core/java/com/android/internal/util/ObservableServiceConnection.java index 3165d293bd91e831718e8cc3e3f5e6958edf4b81..45256fd26cba69904e5d0a9d70e472cb2b379d0a 100644 --- a/core/java/com/android/internal/util/ObservableServiceConnection.java +++ b/core/java/com/android/internal/util/ObservableServiceConnection.java @@ -164,6 +164,13 @@ public class ObservableServiceConnection implements ServiceConnection { mFlags = flags; } + /** + * Executes code on the executor specified at construction. + */ + public void execute(Runnable runnable) { + mExecutor.execute(runnable); + } + /** * Initiate binding to the service. * diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 79c519645a24dd92e608cdde3e0e546c42b5a2c4..3a393b689717538100faab167d499cf35b6530e3 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,7 +1,7 @@ package com.android.internal.util; import static android.content.Intent.ACTION_USER_SWITCHED; -import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; +import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import android.annotation.NonNull; import android.annotation.Nullable; @@ -11,29 +11,18 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; -import android.graphics.Bitmap; -import android.graphics.ColorSpace; -import android.graphics.Insets; -import android.graphics.ParcelableColorSpace; -import android.graphics.Rect; -import android.hardware.HardwareBuffer; import android.net.Uri; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.os.Parcel; -import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.WindowManager.ScreenshotSource; -import android.view.WindowManager.ScreenshotType; import com.android.internal.annotations.VisibleForTesting; -import java.util.Objects; import java.util.function.Consumer; public class ScreenshotHelper { @@ -41,212 +30,6 @@ public class ScreenshotHelper { public static final int SCREENSHOT_MSG_URI = 1; public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2; - /** - * Describes a screenshot request. - */ - public static class ScreenshotRequest implements Parcelable { - @ScreenshotType - private final int mType; - - @ScreenshotSource - private final int mSource; - - private final Bundle mBitmapBundle; - private final Rect mBoundsInScreen; - private final Insets mInsets; - private final int mTaskId; - private final int mUserId; - private final ComponentName mTopComponent; - - - public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source) { - this(type, source, /* topComponent */ null); - } - - public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source, - ComponentName topComponent) { - this(type, - source, - /* bitmapBundle*/ null, - /* boundsInScreen */ null, - /* insets */ null, - /* taskId */ -1, - /* userId */ -1, - topComponent); - } - - public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source, - Bundle bitmapBundle, Rect boundsInScreen, Insets insets, int taskId, int userId, - ComponentName topComponent) { - mType = type; - mSource = source; - mBitmapBundle = bitmapBundle; - mBoundsInScreen = boundsInScreen; - mInsets = insets; - mTaskId = taskId; - mUserId = userId; - mTopComponent = topComponent; - } - - ScreenshotRequest(Parcel in) { - mType = in.readInt(); - mSource = in.readInt(); - if (in.readInt() == 1) { - mBitmapBundle = in.readBundle(getClass().getClassLoader()); - mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader(), Rect.class); - mInsets = in.readParcelable(Insets.class.getClassLoader(), Insets.class); - mTaskId = in.readInt(); - mUserId = in.readInt(); - mTopComponent = in.readParcelable(ComponentName.class.getClassLoader(), - ComponentName.class); - } else { - mBitmapBundle = null; - mBoundsInScreen = null; - mInsets = null; - mTaskId = -1; - mUserId = -1; - mTopComponent = null; - } - } - - @ScreenshotType - public int getType() { - return mType; - } - - @ScreenshotSource - public int getSource() { - return mSource; - } - - public Bundle getBitmapBundle() { - return mBitmapBundle; - } - - public Rect getBoundsInScreen() { - return mBoundsInScreen; - } - - public Insets getInsets() { - return mInsets; - } - - public int getTaskId() { - return mTaskId; - } - - public int getUserId() { - return mUserId; - } - - public ComponentName getTopComponent() { - return mTopComponent; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mType); - dest.writeInt(mSource); - if (mBitmapBundle == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - dest.writeBundle(mBitmapBundle); - dest.writeParcelable(mBoundsInScreen, 0); - dest.writeParcelable(mInsets, 0); - dest.writeInt(mTaskId); - dest.writeInt(mUserId); - dest.writeParcelable(mTopComponent, 0); - } - } - - @NonNull - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - - @Override - public ScreenshotRequest createFromParcel(Parcel source) { - return new ScreenshotRequest(source); - } - - @Override - public ScreenshotRequest[] newArray(int size) { - return new ScreenshotRequest[size]; - } - }; - } - - /** - * Bundler used to convert between a hardware bitmap and a bundle without copying the internal - * content. This is expected to be used together with {@link #provideScreenshot} to handle a - * hardware bitmap as a screenshot. - */ - public static final class HardwareBitmapBundler { - private static final String KEY_BUFFER = "bitmap_util_buffer"; - private static final String KEY_COLOR_SPACE = "bitmap_util_color_space"; - - private HardwareBitmapBundler() { - } - - /** - * Creates a Bundle that represents the given Bitmap. - *

    The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid - * copies when passing across processes, only pass to processes you trust. - * - *

    Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the - * returned Bundle should be treated as a standalone object. - * - * @param bitmap to convert to bundle - * @return a Bundle representing the bitmap, should only be parsed by - * {@link #bundleToHardwareBitmap(Bundle)} - */ - public static Bundle hardwareBitmapToBundle(Bitmap bitmap) { - if (bitmap.getConfig() != Bitmap.Config.HARDWARE) { - throw new IllegalArgumentException( - "Passed bitmap must have hardware config, found: " + bitmap.getConfig()); - } - - // Bitmap assumes SRGB for null color space - ParcelableColorSpace colorSpace = - bitmap.getColorSpace() == null - ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) - : new ParcelableColorSpace(bitmap.getColorSpace()); - - Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer()); - bundle.putParcelable(KEY_COLOR_SPACE, colorSpace); - - return bundle; - } - - /** - * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .} - * - *

    This Bitmap contains the HardwareBuffer from the original caller, be careful passing - * this Bitmap on to any other source. - * - * @param bundle containing the bitmap - * @return a hardware Bitmap - */ - public static Bitmap bundleToHardwareBitmap(Bundle bundle) { - if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) { - throw new IllegalArgumentException("Bundle does not contain a hardware bitmap"); - } - - 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()); - } - } - private static final String TAG = "ScreenshotHelper"; // Time until we give up on the screenshot & show an error instead. @@ -277,68 +60,53 @@ public class ScreenshotHelper { /** * Request a screenshot be taken. *

    - * Added to support reducing unit test duration; the method variant without a timeout argument - * is recommended for general use. + * Convenience method for taking a full screenshot with provided source. * - * @param type The type of screenshot, defined by {@link ScreenshotType} - * @param source The source of the screenshot request, defined by {@link ScreenshotSource} - * @param handler used to process messages received from the screenshot service + * @param source source of the screenshot request, defined by {@link + * ScreenshotSource} + * @param handler used to process messages received from the screenshot service * @param completionConsumer receives the URI of the captured screenshot, once saved or - * null if no screenshot was saved + * null if no screenshot was saved */ - public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source, - @NonNull Handler handler, @Nullable Consumer completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source); - takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, - completionConsumer); + public void takeScreenshot(@ScreenshotSource int source, @NonNull Handler handler, + @Nullable Consumer completionConsumer) { + ScreenshotRequest request = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, source).build(); + takeScreenshot(request, handler, completionConsumer); } /** * Request a screenshot be taken. *

    - * Added to support reducing unit test duration; the method variant without a timeout argument - * is recommended for general use. * - * @param type The type of screenshot, defined by {@link ScreenshotType} - * @param source The source of the screenshot request, defined by {@link ScreenshotSource} - * @param handler used to process messages received from the screenshot service - * @param timeoutMs time limit for processing, intended only for testing + * @param request description of the screenshot request, either for taking a + * screenshot or + * providing a bitmap + * @param handler used to process messages received from the screenshot service * @param completionConsumer receives the URI of the captured screenshot, once saved or - * null if no screenshot was saved + * null if no screenshot was saved */ - @VisibleForTesting - public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source, - @NonNull Handler handler, long timeoutMs, @Nullable Consumer completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source); - takeScreenshot(handler, screenshotRequest, timeoutMs, completionConsumer); + public void takeScreenshot(ScreenshotRequest request, @NonNull Handler handler, + @Nullable Consumer completionConsumer) { + takeScreenshotInternal(request, handler, completionConsumer, SCREENSHOT_TIMEOUT_MS); } /** - * Request that provided image be handled as if it was a screenshot. + * Request a screenshot be taken. + *

    + * Added to support reducing unit test duration; the method variant without a timeout argument + * is recommended for general use. * - * @param screenshotBundle Bundle containing the buffer and color space of the screenshot. - * @param boundsInScreen The bounds in screen coordinates that the bitmap originated from. - * @param insets The insets that the image was shown with, inside the screen bounds. - * @param taskId The taskId of the task that the screen shot was taken of. - * @param userId The userId of user running the task provided in taskId. - * @param topComponent The component name of the top component running in the task. - * @param source The source of the screenshot request, defined by {@link ScreenshotSource} - * @param handler A handler used in case the screenshot times out + * @param request description of the screenshot request, either for taking a + * screenshot or providing a bitmap + * @param handler used to process messages received from the screenshot service + * @param timeoutMs time limit for processing, intended only for testing * @param completionConsumer receives the URI of the captured screenshot, once saved or - * null if no screenshot was saved + * null if no screenshot was saved */ - public void provideScreenshot(@NonNull Bundle screenshotBundle, @NonNull Rect boundsInScreen, - @NonNull Insets insets, int taskId, int userId, ComponentName topComponent, - @ScreenshotSource int source, @NonNull Handler handler, - @Nullable Consumer completionConsumer) { - ScreenshotRequest screenshotRequest = new ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, - source, screenshotBundle, boundsInScreen, insets, taskId, userId, topComponent); - takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, completionConsumer); - } - - private void takeScreenshot(@NonNull Handler handler, - ScreenshotRequest screenshotRequest, long timeoutMs, - @Nullable Consumer completionConsumer) { + @VisibleForTesting + public void takeScreenshotInternal(ScreenshotRequest request, @NonNull Handler handler, + @Nullable Consumer completionConsumer, long timeoutMs) { synchronized (mScreenshotLock) { final Runnable mScreenshotTimeout = () -> { @@ -354,7 +122,7 @@ public class ScreenshotHelper { } }; - Message msg = Message.obtain(null, 0, screenshotRequest); + Message msg = Message.obtain(null, 0, request); Handler h = new Handler(handler.getLooper()) { @Override @@ -471,5 +239,4 @@ public class ScreenshotHelper { Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT); } - } diff --git a/core/java/com/android/internal/util/ScreenshotRequest.aidl b/core/java/com/android/internal/util/ScreenshotRequest.aidl new file mode 100644 index 0000000000000000000000000000000000000000..b08905dd0c9a5b69e6f576d649ad64dabb18a500 --- /dev/null +++ b/core/java/com/android/internal/util/ScreenshotRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +parcelable ScreenshotRequest; \ No newline at end of file diff --git a/core/java/com/android/internal/util/ScreenshotRequest.java b/core/java/com/android/internal/util/ScreenshotRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c8b7defb5276e84003e3a5f079c4e7d5b15d35f9 --- /dev/null +++ b/core/java/com/android/internal/util/ScreenshotRequest.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.os.UserHandle.USER_NULL; +import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.Insets; +import android.graphics.ParcelableColorSpace; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.view.WindowManager; + +import java.util.Objects; + +/** + * Describes a screenshot request. + */ +public class ScreenshotRequest implements Parcelable { + private static final String TAG = "ScreenshotRequest"; + + @WindowManager.ScreenshotType + private final int mType; + @WindowManager.ScreenshotSource + private final int mSource; + private final ComponentName mTopComponent; + private final int mTaskId; + private final int mUserId; + private final Bitmap mBitmap; + private final Rect mBoundsInScreen; + private final Insets mInsets; + + private ScreenshotRequest( + @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source, + ComponentName topComponent, int taskId, int userId, + Bitmap bitmap, Rect boundsInScreen, Insets insets) { + mType = type; + mSource = source; + mTopComponent = topComponent; + mTaskId = taskId; + mUserId = userId; + mBitmap = bitmap; + mBoundsInScreen = boundsInScreen; + mInsets = insets; + } + + ScreenshotRequest(Parcel in) { + mType = in.readInt(); + mSource = in.readInt(); + mTopComponent = in.readTypedObject(ComponentName.CREATOR); + mTaskId = in.readInt(); + mUserId = in.readInt(); + mBitmap = HardwareBitmapBundler.bundleToHardwareBitmap(in.readTypedObject(Bundle.CREATOR)); + mBoundsInScreen = in.readTypedObject(Rect.CREATOR); + mInsets = in.readTypedObject(Insets.CREATOR); + } + + @WindowManager.ScreenshotType + public int getType() { + return mType; + } + + @WindowManager.ScreenshotSource + public int getSource() { + return mSource; + } + + public Bitmap getBitmap() { + return mBitmap; + } + + public Rect getBoundsInScreen() { + return mBoundsInScreen; + } + + public Insets getInsets() { + return mInsets; + } + + public int getTaskId() { + return mTaskId; + } + + public int getUserId() { + return mUserId; + } + + public ComponentName getTopComponent() { + return mTopComponent; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mSource); + dest.writeTypedObject(mTopComponent, 0); + dest.writeInt(mTaskId); + dest.writeInt(mUserId); + dest.writeTypedObject(HardwareBitmapBundler.hardwareBitmapToBundle(mBitmap), 0); + dest.writeTypedObject(mBoundsInScreen, 0); + dest.writeTypedObject(mInsets, 0); + } + + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public ScreenshotRequest createFromParcel(Parcel source) { + return new ScreenshotRequest(source); + } + + @Override + public ScreenshotRequest[] newArray(int size) { + return new ScreenshotRequest[size]; + } + }; + + /** + * Builder class for {@link ScreenshotRequest} objects. + */ + public static class Builder { + @WindowManager.ScreenshotType + private final int mType; + + @WindowManager.ScreenshotSource + private final int mSource; + + private Bitmap mBitmap; + private Rect mBoundsInScreen; + private Insets mInsets = Insets.NONE; + private int mTaskId = INVALID_TASK_ID; + private int mUserId = USER_NULL; + private ComponentName mTopComponent; + + /** + * Begin building a ScreenshotRequest. + * + * @param type The type of the screenshot request, defined by {@link + * WindowManager.ScreenshotType} + * @param source The source of the screenshot request, defined by {@link + * WindowManager.ScreenshotSource} + */ + public Builder( + @WindowManager.ScreenshotType int type, + @WindowManager.ScreenshotSource int source) { + if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE) { + throw new IllegalArgumentException("Invalid screenshot type requested!"); + } + mType = type; + mSource = source; + } + + /** + * Construct a new {@link ScreenshotRequest} with the set parameters. + */ + public ScreenshotRequest build() { + if (mType == TAKE_SCREENSHOT_FULLSCREEN && mBitmap != null) { + Log.w(TAG, "Bitmap provided, but request is fullscreen. Bitmap will be ignored."); + } + if (mType == TAKE_SCREENSHOT_PROVIDED_IMAGE && mBitmap == null) { + throw new IllegalStateException( + "Request is PROVIDED_IMAGE, but no bitmap is provided!"); + } + + return new ScreenshotRequest(mType, mSource, mTopComponent, mTaskId, mUserId, mBitmap, + mBoundsInScreen, mInsets); + } + + /** + * Set the top component associated with this request. + * + * @param topComponent The component name of the top component running in the task. + */ + public Builder setTopComponent(ComponentName topComponent) { + mTopComponent = topComponent; + return this; + } + + /** + * Set the task id associated with this request. + * + * @param taskId The taskId of the task that the screenshot was taken of. + */ + public Builder setTaskId(int taskId) { + mTaskId = taskId; + return this; + } + + /** + * Set the user id associated with this request. + * + * @param userId The userId of user running the task provided in taskId. + */ + public Builder setUserId(int userId) { + mUserId = userId; + return this; + } + + /** + * Set the bitmap associated with this request. + * + * @param bitmap The provided screenshot. + */ + public Builder setBitmap(Bitmap bitmap) { + mBitmap = bitmap; + return this; + } + + /** + * Set the bounds for the provided bitmap. + * + * @param bounds The bounds in screen coordinates that the bitmap originated from. + */ + public Builder setBoundsOnScreen(Rect bounds) { + mBoundsInScreen = bounds; + return this; + } + + /** + * Set the insets for the provided bitmap. + * + * @param insets The insets that the image was shown with, inside the screen bounds. + */ + public Builder setInsets(@NonNull Insets insets) { + mInsets = insets; + return this; + } + } + + /** + * Bundler used to convert between a hardware bitmap and a bundle without copying the internal + * content. This is used together with a fully-defined ScreenshotRequest to handle a hardware + * bitmap as a screenshot. + */ + private static final class HardwareBitmapBundler { + private static final String KEY_BUFFER = "bitmap_util_buffer"; + private static final String KEY_COLOR_SPACE = "bitmap_util_color_space"; + + private HardwareBitmapBundler() { + } + + /** + * Creates a Bundle that represents the given Bitmap. + *

    The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will + * avoid + * copies when passing across processes, only pass to processes you trust. + * + *

    Returns a new Bundle rather than modifying an exiting one to avoid key collisions, + * the + * returned Bundle should be treated as a standalone object. + * + * @param bitmap to convert to bundle + * @return a Bundle representing the bitmap, should only be parsed by + * {@link #bundleToHardwareBitmap(Bundle)} + */ + private static Bundle hardwareBitmapToBundle(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + if (bitmap.getConfig() != Bitmap.Config.HARDWARE) { + throw new IllegalArgumentException( + "Passed bitmap must have hardware config, found: " + + bitmap.getConfig()); + } + + // Bitmap assumes SRGB for null color space + ParcelableColorSpace colorSpace = + bitmap.getColorSpace() == null + ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) + : new ParcelableColorSpace(bitmap.getColorSpace()); + + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer()); + bundle.putParcelable(KEY_COLOR_SPACE, colorSpace); + + return bundle; + } + + /** + * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)}. + * + *

    This Bitmap contains the HardwareBuffer from the original caller, be careful + * passing + * this Bitmap on to any other source. + * + * @param bundle containing the bitmap + * @return a hardware Bitmap + */ + private static Bitmap bundleToHardwareBitmap(Bundle bundle) { + if (bundle == null) { + return null; + } + if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) { + throw new IllegalArgumentException("Bundle does not contain a hardware bitmap"); + } + + 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/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 869da1ffcb6ee00295962ce3c32721241cb6e4b5..058c6ec4d13c7b63ab0859e3cce75145b14a8fc9 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -106,7 +106,9 @@ public final class RotationPolicy { * Enables or disables rotation lock from the system UI toggle. */ public static void setRotationLock(Context context, final boolean enabled) { - final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION; + final int rotation = areAllRotationsAllowed(context) + || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION + : NATURAL_ROTATION; setRotationLockAtAngle(context, enabled, rotation); } @@ -139,6 +141,11 @@ public final class RotationPolicy { return context.getResources().getBoolean(R.bool.config_allowAllRotations); } + private static boolean useCurrentRotationOnRotationLockChange(Context context) { + return context.getResources().getBoolean( + R.bool.config_useCurrentRotationOnRotationLockChange); + } + private static void setRotationLock(final boolean enabled, final int rotation) { AsyncTask.execute(new Runnable() { @Override diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 2dfe89397ea5e5ec3f0cf588a6259b1f199ce5b8..5b2c441f95c9c982153fdf1f6b1b16cf934ac539 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -170,6 +170,8 @@ public class LockPatternUtils { private static final String LOCK_SCREEN_OWNER_INFO_ENABLED = Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; + private static final String LOCK_PIN_ENHANCED_PRIVACY = "pin_enhanced_privacy"; + private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info"; private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; @@ -998,6 +1000,27 @@ public class LockPatternUtils { return getString(Settings.Secure.LOCK_PATTERN_VISIBLE, userId) != null; } + /** + * @return Whether enhanced pin privacy is enabled. + */ + public boolean isPinEnhancedPrivacyEnabled(int userId) { + return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId); + } + + /** + * Set whether enhanced pin privacy is enabled. + */ + public void setPinEnhancedPrivacyEnabled(boolean enabled, int userId) { + setBoolean(LOCK_PIN_ENHANCED_PRIVACY, enabled, userId); + } + + /** + * @return Whether enhanced pin privacy was ever chosen. + */ + public boolean isPinEnhancedPrivacyEverChosen(int userId) { + return getString(LOCK_PIN_ENHANCED_PRIVACY, userId) != null; + } + /** * Set whether the visible password is enabled for cryptkeeper screen. */ diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index a8abe50a97554ded682f189de4506014ae1b7560..2a670e865cedbf498076dd567e016243b2227d73 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -556,7 +556,8 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, jin // connect to camera service static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint cameraId, jstring clientPackageName, - jboolean overrideToPortrait) { + jboolean overrideToPortrait, + jboolean forceSlowJpegMode) { // Convert jstring to String16 const char16_t *rawClientName = reinterpret_cast( env->GetStringChars(clientPackageName, NULL)); @@ -568,7 +569,7 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj int targetSdkVersion = android_get_application_target_sdk_version(); sp camera = Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID, - targetSdkVersion, overrideToPortrait); + targetSdkVersion, overrideToPortrait, forceSlowJpegMode); if (camera == NULL) { return -EACCES; } @@ -1054,7 +1055,7 @@ static const JNINativeMethod camMethods[] = { {"getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras}, {"_getCameraInfo", "(IZLandroid/hardware/Camera$CameraInfo;)V", (void *)android_hardware_Camera_getCameraInfo}, - {"native_setup", "(Ljava/lang/Object;ILjava/lang/String;Z)I", + {"native_setup", "(Ljava/lang/Object;ILjava/lang/String;ZZ)I", (void *)android_hardware_Camera_native_setup}, {"native_release", "()V", (void *)android_hardware_Camera_release}, {"setPreviewSurface", "(Landroid/view/Surface;)V", diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 8c23b214b8fe074d70b746ff2347dfa0267ad7dc..206ad17e3c4b9f6af8522e181fac48c9cae9f25c 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -93,6 +93,7 @@ static struct configuration_offsets_t { jfieldID mScreenWidthDpOffset; jfieldID mScreenHeightDpOffset; jfieldID mScreenLayoutOffset; + jfieldID mUiMode; } gConfigurationOffsets; static struct arraymap_offsets_t { @@ -1027,10 +1028,11 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); env->SetIntField(result, gConfigurationOffsets.mScreenLayoutOffset, config.screenLayout); + env->SetIntField(result, gConfigurationOffsets.mUiMode, config.uiMode); return result; } -static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { +static jobjectArray GetSizeAndUiModeConfigurations(JNIEnv* env, jlong ptr) { ScopedLock assetmanager(AssetManagerFromLong(ptr)); auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); @@ -1057,6 +1059,14 @@ static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, j return array; } +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + return GetSizeAndUiModeConfigurations(env, ptr); +} + +static jobjectArray NativeGetSizeAndUiModeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + return GetSizeAndUiModeConfigurations(env, ptr); +} + static jintArray NativeAttributeResolutionStack( JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, jint xml_style_res, @@ -1487,6 +1497,8 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", (void*)NativeGetSizeConfigurations}, + {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeAndUiModeConfigurations}, // Style attribute related methods. {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack}, @@ -1565,6 +1577,7 @@ int register_android_content_AssetManager(JNIEnv* env) { GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); gConfigurationOffsets.mScreenLayoutOffset = GetFieldIDOrDie(env, configurationClass, "screenLayout", "I"); + gConfigurationOffsets.mUiMode = GetFieldIDOrDie(env, configurationClass, "uiMode", "I"); jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap"); gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index b1610d790222592246a65bc499349ce83e873999..8952f37b14693b1a10313637932009e2f3042af9 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -428,7 +428,7 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return 0; } else if (err != NO_ERROR) { - jniThrowException(env, OutOfResourcesException, NULL); + jniThrowException(env, OutOfResourcesException, statusToString(err).c_str()); return 0; } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 556636ddd210694a73c3bd71f9cbe7ef48702fe1..d443270575d3b67a4d0625a3aba50d68eea5a78a 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -89,6 +89,8 @@ message SecureSettingsProto { // Setting for accessibility magnification for following typing. optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto contrast_level = 44 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Settings for font scaling + optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 8e4006aa6861498ce70a069b31e1b19ad9133b2e..e029af4f9819c82b527fc74ba007e0fdecf17229 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -110,11 +110,20 @@ message ManagedServicesProto { // All of this type/caption enabled for current profiles. repeated android.content.ComponentNameProto enabled = 3; - repeated ManagedServiceInfoProto live_services = 4; + // Was: repeated ComponentNameProto, when snoozed services were not per-user-id. + reserved 5; + + message SnoozedServices { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 user_id = 1; + repeated android.content.ComponentNameProto snoozed = 2; + } + // Snoozed for current profiles. - repeated android.content.ComponentNameProto snoozed = 5; + repeated SnoozedServices snoozed = 6; } message RankingHelperProto { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 35dc8e37aaab92644623615d5ddff9d3b0fb1abe..c226112898a931ea495f4704295b363032011d6a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -315,6 +315,7 @@ + @@ -3156,7 +3157,11 @@ android:protectionLevel="normal" /> Should only be requested by the System, should be required by TileService declarations.--> + android:protectionLevel="signature|recents" /> + + diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 50e6f33f628a48169c8332c35acec027c708a3d1..a5ff4706c085900ee637a1119abd5d490443de1d 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -17,6 +17,7 @@ */ --> "Kan gebare vasvang wat op die toestel se vingerafdruksensor uitgevoer word." "Neem skermkiekie" "Kan \'n skermkiekie neem." + "Voorskou, %1$s" "deaktiveer of verander statusbalk" "Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder." "wees die statusbalk" @@ -1839,6 +1840,8 @@ "Bekyk tans volskerm" "Swiep van bo na onder as jy wil uitgaan." "Het dit" + "Draai vir ’n beter aansig" + "Verlaat gedeelde skerm vir ’n beter aansig" "Klaar" "Ure se sirkelglyer" "Minute se sirkelglyer" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 5b64cb539a9d5bc7c5631386739fe279519daf3b..74d22604db9244d03813fd3dd84b0a2f15e08640 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -339,6 +339,7 @@ "በመሣሪያው የጣት አሻራ ዳሳሽ ላይ የተከናወኑ የጣት ምልክቶችን መያዝ ይችላል።" "ቅጽበታዊ ገፅ እይታን ያነሳል" "የማሳያው ቅጽበታዊ ገፅ እይታን ማንሳት ይችላል።" + "ቅድመ ዕይታ፣ %1$s" "የሁኔቴ አሞሌ አቦዝን ወይም ቀይር" "የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡" "የሁኔታ አሞሌ መሆን" @@ -1839,6 +1840,8 @@ "ሙሉ ገፅ በማሳየት ላይ" "ለመውጣት፣ ከላይ ወደታች ጠረግ ያድርጉ።" "ገባኝ" + "ለተሻለ ዕይታ ያሽከርክሩ" + "ለተሻለ ዕይታ የተከፈለ ማያ ገጽን ትተው ይውጡ" "ተከናውኗል" "የሰዓታት ክብ ተንሸራታች" "የደቂቃዎች ክብ ተንሸራታች" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 007ae736798045f32052ae98040cdee430502fa3..c61acba2280f13a7199fead4df2c724ec70689b8 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -343,6 +343,7 @@ "يمكن أن تلتقط الإيماءات من أداة استشعار بصمة الإصبع في الجهاز." "أخذ لقطة شاشة" "يمكن أخذ لقطة شاشة." + "نسخة حصرية، \"%1$s\"" "إيقاف شريط الحالة أو تعديله" "للسماح للتطبيق بإيقاف شريط الحالة أو إضافة رموز نظام وإزالتها." "العمل كشريط للحالة" @@ -1843,6 +1844,8 @@ "جارٍ العرض بملء الشاشة" "للخروج، مرر بسرعة من أعلى إلى أسفل." "حسنًا" + "يمكنك تدوير الجهاز لرؤية شاشة معاينة الكاميرا بشكل أوضح." + "يمكنك الخروج من وضع \"تقسيم الشاشة\" لرؤية شاشة معاينة الكاميرا بشكل أوضح." "تم" "شريط التمرير الدائري للساعات" "شريط التمرير الدائري للدقائق" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 7c6cdea79668168ed8164deeb8c846e447e0a7b5..321623508a082cad4d5ecb84a43bd257f78edd95 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -339,6 +339,7 @@ "ডিভাইচটোৰ ফিংগাৰপ্ৰিণ্ট ছেন্সৰত দিয়া নিৰ্দেশ বুজিব পাৰে।" "স্ক্ৰীনশ্বট লওক" "ডিছপ্লে’খনৰ এটা স্ক্ৰীনশ্বট ল\'ব পাৰে।" + "পূৰ্বদৰ্শন কৰক, %1$s" "স্থিতি দণ্ড অক্ষম কৰক বা সলনি কৰক" "স্থিতি দণ্ড অক্ষম কৰিবলৈ বা ছিষ্টেম আইকন আঁতৰাবলৈ এপ্‌টোক অনুমতি দিয়ে।" "স্থিতি দণ্ড হ\'ব পাৰে" @@ -1839,6 +1840,8 @@ "স্ক্ৰীন পূৰ্ণৰূপত চাই আছে" "বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।" "বুজি পালোঁ" + "ভালকৈ চাবলৈ ঘূৰাওক" + "ভালকৈ চাবলৈ বিভাজিত স্ক্ৰীনৰ পৰা বাহিৰ হওক" "সম্পন্ন কৰা হ’ল" "ঘড়ীৰ বৃত্তাকাৰ শ্লাইডাৰ" "মিনিটৰ বৃত্তাকাৰ শ্লাইডাৰ" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index b6df531c5da43f0fe954e72de8c853a975aa882e..50de22a2487390990f15859eaa57e06d7f744f75 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -339,6 +339,7 @@ "Cihazların barmaq izi sensorunda olan işarələri əldə edə bilər." "Ekran şəkli çəkin" "Ekran şəkli çəkilə bilər." + "Önizləmə, %1$s" "status panelini deaktivləşdir və ya dəyişdir" "Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir." "status paneli edin" @@ -1839,6 +1840,8 @@ "Tam ekrana baxış" "Çıxmaq üçün yuxarıdan aşağı sürüşdürün." "Anladım" + "Daha yaxşı görünüş üçün fırladın" + "Daha yaxşı görünüş üçün bölünmüş ekrandan çıxın" "Hazırdır" "Dairəvi saat slayderi" "Dairəvi dəqiqə slayderi" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index c7ea7703d56ac645f11a5dfd7cef1c9a6d68a84b..487e9aa0e6ce8917fb8819716fd2ff617774971f 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -340,6 +340,7 @@ "Može da registruje pokrete na senzoru za otisak prsta na uređaju." "Napravi snimak ekrana" "Može da napravi snimak ekrana." + "Pregled, %1$s" "onemogućavanje ili izmena statusne trake" "Dozvoljava aplikaciji da onemogući statusnu traku ili da dodaje i uklanja sistemske ikone." "funkcionisanje kao statusna traka" @@ -1840,6 +1841,8 @@ "Prikazuje se ceo ekran" "Da biste izašli, prevucite nadole odozgo." "Važi" + "Rotirajte radi boljeg prikaza" + "Izađite iz podeljenog ekrana radi boljeg prikaza" "Gotovo" "Kružni klizač za sate" "Kružni klizač za minute" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 387cc7fc789770a5af3f0b8ef332632fc15c5364..4d655c323716735cafcc27e5a4680ede5b91fa17 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -341,6 +341,7 @@ "Можа распазнаваць жэсты на сканеры адбіткаў пальцаў прылады." "Зрабіць здымак экрана" "Можна зрабіць здымак экрана." + "Перадпрагляд, %1$s" "адключаць ці змяняць радок стану" "Дазваляе прыкладанням адключаць радок стану або дадаваць і выдаляць сістэмныя значкі." "быць панэллю стану" @@ -1841,6 +1842,8 @@ "Прагляд у поўнаэкранным рэжыме" "Для выхаду правядзіце зверху ўніз." "Зразумела" + "Павярнуць для лепшага прагляду" + "Выйсці з рэжыму падзеленага экрана для лепшага прагляду" "Гатова" "Кругавы паўзунок гадзін" "Кругавы паўзунок хвілін" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 23b052881532f8f18174a22b515426aabea2c02a..fe56b3d58dd27538405ca0975725be3a151c9536 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -339,6 +339,7 @@ "Може да улавя жестовете, извършени върху сензора за отпечатъци на устройството." "Създаване на екранна снимка" "Може да създава екранни снимки." + "Визуализация на „%1$s“" "деактивиране или промяна на лентата на състоянието" "Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони." "изпълняване на ролята на лента на състоянието" @@ -1839,6 +1840,8 @@ "Изглед на цял екран" "За изход плъзнете пръст надолу от горната част." "Разбрах" + "Завъртете за по-добър изглед" + "Излезте от разделения екран за по-добър изглед" "Готово" "Кръгов плъзгач за часовете" "Кръгов плъзгач за минутите" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 55cb8a6488419fb99c6cbe12025a75b061519f81..e34e10b6749c2da466cad3524cab735dfb81e1a2 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -339,6 +339,7 @@ "ডিভাইসের আঙ্গুলের ছাপের সেন্সরের উপরে ইঙ্গিত করলে বুঝতে পারে।" "স্ক্রিনশট নিন" "ডিসপ্লের একটি স্ক্রিনশট নিতে পারেন।" + "প্রিভিউ, %1$s" "স্ট্যাটাস বার নিষ্ক্রিয় অথবা সংশোধন করে" "অ্যাপ্লিকেশনকে স্ট্যাটাস বার অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷" "স্থিতি দন্ডে থাকুন" @@ -1839,6 +1840,8 @@ "পূর্ণ স্ক্রিনে দেখা হচ্ছে" "প্রস্থান করতে উপর থেকে নিচের দিকে সোয়াইপ করুন" "বুঝেছি" + "আরও ভাল ক্যামেরা ভিউ পাওয়ার জন্য ঘোরান" + "আরও ভাল ক্যামেরা ভিউ পাওয়ার জন্য স্প্লিট স্ক্রিন থেকে বেরিয়ে আসুন" "সম্পন্ন হয়েছে" "বৃত্তাকার ঘণ্টা নির্বাচকের স্লাইডার" "বৃত্তাকার মিনিট নির্বাচকের স্লাইডার" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 7ef64075447984930f3478da2f181a930afdb5d6..499eaa728293ebc9a97bc5ec5ccc5a886eefe141 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -340,6 +340,7 @@ "Može zabilježiti pokrete na senzoru za otisak prsta uređaja." "praviti snimke ekrana" "Može napraviti snimak ekrana." + "Pregled, %1$s" "onemogućavanje ili mijenjanje statusne trake" "Dozvoljava aplikaciji onemogućavanje statusne trake ili dodavanje i uklanjanje sistemskih ikona." "funkcioniranje u vidu statusne trake" @@ -1840,6 +1841,8 @@ "Prikazuje se cijeli ekran" "Da izađete, prevucite odozgo nadolje." "Razumijem" + "Rotirajte za bolji prikaz" + "Izađite iz podijeljenog ekrana za bolji prikaz" "Gotovo" "Kružni klizač za odabir sata" "Kružni klizač za minute" diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index a0541c8945bff0c773d7c058c325c44fe11e1aee..5a655638d221bde73e26118764368bc23ef564ea 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -340,6 +340,7 @@ "Pot capturar els gestos fets en el sensor d\'empremtes digitals del dispositiu." "Fer una captura de pantalla" "Pot fer una captura de la pantalla." + "Previsualitza, %1$s" "desactivar o modificar la barra d\'estat" "Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema." "aparèixer a la barra d\'estat" @@ -1840,6 +1841,8 @@ "Mode de pantalla completa" "Per sortir, llisca cap avall des de la part superior." "Entesos" + "Gira per a una millor visualització" + "Surt de la pantalla dividida per a una millor visualització" "Fet" "Control circular de les hores" "Control circular dels minuts" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 24cc2a8a446f27986f08a5bd878f44dba65b9706..74d487d988c74d57cf095516fe535c96de8255c0 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -341,6 +341,7 @@ "Dokáže rozpoznat gesta zadaná na snímači otisků prstů." "Pořídit snímek obrazovky" "Může pořídit snímek obrazovky." + "Náhled, %1$s" "zakázání či změny stavového řádku" "Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony." "vydávání se za stavový řádek" @@ -1841,6 +1842,8 @@ "Zobrazení celé obrazovky" "Režim ukončíte přejetím prstem shora dolů." "Rozumím" + "Otočte obrazovku, abyste lépe viděli" + "Ukončete režim rozdělené obrazovky, abyste lépe viděli" "Hotovo" "Kruhový posuvník hodin" "Kruhový posuvník minut" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 5152854df825ed07aca619ed0852bf4542fb9f1c..72ca9f067f6ca7342a79675360b248188feea9c2 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -339,6 +339,7 @@ "Kan registrere bevægelser, der foretages på enhedens fingeraftrykssensor." "Tag screenshot" "Kan tage et screenshot af skærmen." + "Preview, %1$s" "deaktivere eller redigere statuslinje" "Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner." "vær statusbjælken" @@ -1839,6 +1840,8 @@ "Visning i fuld skærm" "Stryg ned fra toppen for at afslutte." "OK, det er forstået" + "Roter, og få en bedre visning" + "Afslut opdelt skærm, og få en bedre visning" "Udfør" "Cirkulær timevælger" "Cirkulær minutvælger" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 462ca3f49732bd13247022248773fe0257ba6d99..f9d7ffb208c7474a1bde12d22795eb846cd30e2d 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -339,6 +339,7 @@ "Erfasst Touch-Gesten auf dem Fingerabdrucksensor des Geräts." "Screenshot erstellen" "Es kann ein Screenshot des Displays erstellt werden." + "Vorschau – %1$s" "Statusleiste deaktivieren oder ändern" "Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen" "Statusleiste darstellen" @@ -1839,6 +1840,8 @@ "Vollbildmodus wird aktiviert" "Zum Beenden von oben nach unten wischen" "Ok" + "Drehen, um die Ansicht zu verbessern" + "Splitscreen-Modus beenden, um die Ansicht zu verbessern" "Fertig" "Kreisförmiger Schieberegler für Stunden" "Kreisförmiger Schieberegler für Minuten" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index c8743c7bd25bbf199b2c4b18970782e1edf13e4d..d002d3931b7e41a898473f419096aa459235d6d0 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -339,6 +339,7 @@ "Μπορεί να αναγνωρίσει κινήσεις που εκτελούνται στον αισθητήρα δακτυλικού αποτυπώματος της συσκευής." "Λήψη στιγμιότυπου οθόνης" "Μπορεί να τραβήξει στιγμιότυπο της οθόνης." + "Προεπισκόπηση, %1$s" "απενεργοποιεί ή να τροποποιεί την γραμμή κατάστασης" "Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος." "ορίζεται ως γραμμή κατάστασης" @@ -1839,6 +1840,8 @@ "Προβολή σε πλήρη οθόνη" "Για έξοδο, σύρετε προς τα κάτω από το επάνω μέρος." "Το κατάλαβα" + "Περιστρέψτε την οθόνη για καλύτερη προβολή" + "Εξέλθετε από τον διαχωρισμό οθόνης για καλύτερη προβολή" "Τέλος" "Κυκλικό ρυθμιστικό ωρών" "Κυκλικό ρυθμιστικό λεπτών" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index c16471ec471c990f5f69c13f062ad4138834ca6f..d2a2a880fb76fbd812533bcb96ca04db2104b9bc 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -339,6 +339,7 @@ "Can capture gestures performed on the device\'s fingerprint sensor." "Take screenshot" "Can take a screenshot of the display." + "Preview, %1$s" "disable or modify status bar" "Allows the app to disable the status bar or add and remove system icons." "be the status bar" @@ -1839,6 +1840,8 @@ "Viewing full screen" "To exit, swipe down from the top." "Got it" + "Rotate for a better view" + "Exit split screen for a better view" "Done" "Hours circular slider" "Minutes circular slider" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index b56dc75c99a9e004f446cbdce876fab67c990819..4916056c1ada3a5226ecf526ce5886b0bfbd2901 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -339,6 +339,7 @@ "Can capture gestures performed on the device\'s fingerprint sensor." "Take screenshot" "Can take a screenshot of the display." + "Preview, %1$s" "disable or modify status bar" "Allows the app to disable the status bar or add and remove system icons." "be the status bar" @@ -1839,6 +1840,8 @@ "Viewing full screen" "To exit, swipe down from the top." "Got it" + "Rotate for a better view" + "Exit split screen for a better view" "Done" "Hours circular slider" "Minutes circular slider" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 95e234bf41bbb21814311492c80305392cd7bf89..f65b407c5e6ffbd70ffe9680dc2f43c83820a276 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -339,6 +339,7 @@ "Can capture gestures performed on the device\'s fingerprint sensor." "Take screenshot" "Can take a screenshot of the display." + "Preview, %1$s" "disable or modify status bar" "Allows the app to disable the status bar or add and remove system icons." "be the status bar" @@ -1839,6 +1840,8 @@ "Viewing full screen" "To exit, swipe down from the top." "Got it" + "Rotate for a better view" + "Exit split screen for a better view" "Done" "Hours circular slider" "Minutes circular slider" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 7658f16f8ef9044a5dc6474781579c841e0d41ac..64cc0f66789e3127f42b4ddc689cdb96372e99bd 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -339,6 +339,7 @@ "Can capture gestures performed on the device\'s fingerprint sensor." "Take screenshot" "Can take a screenshot of the display." + "Preview, %1$s" "disable or modify status bar" "Allows the app to disable the status bar or add and remove system icons." "be the status bar" @@ -1839,6 +1840,8 @@ "Viewing full screen" "To exit, swipe down from the top." "Got it" + "Rotate for a better view" + "Exit split screen for a better view" "Done" "Hours circular slider" "Minutes circular slider" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 46cbcf0b0f5f0ce72ba4b86098264c5cd396475c..601a0f3fa4ecaf1becff54659a28a4709d2b96a3 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -339,6 +339,7 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎Can capture gestures performed on the device\'s fingerprint sensor.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎Take screenshot‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎Can take a screenshot of the display.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎Preview, ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎disable or modify status bar‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎Allows the app to disable the status bar or add and remove system icons.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎be the status bar‎‏‎‎‏‎" @@ -1839,6 +1840,8 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎Viewing full screen‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎To exit, swipe down from the top.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎Got it‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎Rotate for a better view‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎Exit split screen for a better view‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎Done‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎Hours circular slider‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎Minutes circular slider‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index e71e537a20cd2ea9a34c18f36e24c66af80a176e..f30e2e35906a77ee9209a1dc210a651d25a99318 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -340,6 +340,7 @@ "Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo." "Tomar captura de pantalla" "Puede tomar capturas de pantalla." + "Vista previa, %1$s" "desactivar o modificar la barra de estado" "Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema." "aparecer en la barra de estado" @@ -1840,6 +1841,8 @@ "Visualización en pantalla completa" "Para salir, desliza el dedo hacia abajo desde la parte superior." "Entendido" + "Gira la pantalla para obtener una mejor vista" + "Sal de la pantalla dividida para obtener una mejor vista" "Listo" "Control deslizante circular de horas" "Control deslizante circular de minutos" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index beb1181ebb0a09199d4477eaffe52f9d1af451b6..c7163883d1c43050126ecf5df7290aea3bd39b18 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -340,6 +340,7 @@ "Puede capturar los gestos realizados en el sensor de huellas digitales del dispositivo." "Hacer captura" "Puede hacer capturas de la pantalla." + "Vista previa, %1$s" "inhabilitar o modificar la barra de estado" "Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema." "aparecer en la barra de estado" @@ -1840,6 +1841,8 @@ "Modo de pantalla completa" "Para salir, desliza el dedo de arriba abajo." "Entendido" + "Gira la pantalla para verlo mejor" + "Sal de la pantalla dividida para verlo mejor" "Hecho" "Control deslizante circular de horas" "Control deslizante circular de minutos" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 44a18b8383a2249c8c8ab4069316cd1a5fe579bd..9a05ead2b3f16f7e2b3fd4f4109dc478de0e83dc 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -339,6 +339,7 @@ "Teil on võimalik jäädvustada seadme sõrmejäljeanduril tehtud liigutused." "Jäädvusta ekraanipilt" "Saab jäädvustada ekraanipildi." + "Eelvaade, %1$s" "keela või muuda olekuriba" "Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone." "olekuribana kuvamine" @@ -1839,6 +1840,8 @@ "Kuvamine täisekraanil" "Väljumiseks pühkige ülevalt alla." "Selge" + "Pöörake parema vaate jaoks" + "Parema vaate jaoks väljuge jagatud ekraanikuvast" "Valmis" "Ringikujuline tunniliugur" "Ringikujuline minutiliugur" diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index e86f6e34c9ff5e52e8221948bcb6787b4d9847ef..1313278d76576a622888e0833ca4252a185dd619 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -339,6 +339,7 @@ "Gailuaren hatz-marken sentsorean egindako keinuak atzeman ditzake." "Pantaila-argazkiak atera." "Pantaila-argazkiak atera ditzake." + "Aurrebista, %1$s" "desgaitu edo aldatu egoera-barra" "Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei." "bihurtu egoera-barra" @@ -1839,6 +1840,8 @@ "Pantaila osoko ikuspegia" "Irteteko, pasatu hatza goitik behera." "Ados" + "Biratu pantaila ikuspegi hobea lortzeko" + "Irten pantaila zatitutik ikuspegi hobea lortzeko" "Eginda" "Ordua aukeratzeko ikuspegi zirkularra" "Minutuak aukeratzeko ikuspegi zirkularra" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index b24a9f13235c707067466ad9f68c4a1417c6f785..dbbe21de6070e3d2521659ab71b0fadc5bae245c 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -339,6 +339,7 @@ "می‌تواند اشاره‌های اجرا‌شده روی حسگر اثرانگشت دستگاه را ثبت کند." "گرفتن نماگرفت" "می‌تواند از نمایشگر نماگرفت بگیرد." + "پیش‌نما، %1$s" "غیرفعال کردن یا تغییر نوار وضعیت" "‏به برنامه اجازه می‎دهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند." "نوار وضعیت باشد" @@ -1839,6 +1840,8 @@ "مشاهده در حالت تمام صفحه" "برای خروج، انگشتتان را از بالای صفحه به پایین بکشید." "متوجه شدم" + "برای دید بهتر، دستگاه را بچرخانید" + "برای دید بهتر، از صفحهٔ دونیمه خارج شوید" "تمام" "لغزنده دایره‌ای ساعت" "لغزنده دایره‌ای دقیقه" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 5842ca66342211d538a31fb59c28a146e92b540b..2a0b5137a5a5de8709f7a8f46441440c29b4ca36 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -339,6 +339,7 @@ "Voi tallentaa laitteen sormenjälkitunnistimelle tehtyjä eleitä." "Ota kuvakaappaus" "Voi ottaa kuvakaappauksen näytöstä." + "Esikatselu, %1$s" "poista tilapalkki käytöstä tai muokkaa tilapalkkia" "Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita." "sijaita tilapalkissa" @@ -1839,6 +1840,8 @@ "Koko ruudun tilassa" "Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta." "Selvä" + "Kierrä, niin saat paremman näkymän" + "Poistu jaetulta näytöltä, niin saat paremman näkymän" "Valmis" "Tuntien ympyränmuotoinen liukusäädin" "Minuuttien ympyränmuotoinen liukusäädin" diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 7d31f77cbf44e596e74a6c82e64751ebc93b5472..ac3500ff02975670e0fac07664561e2ea9beccd1 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -340,6 +340,7 @@ "Peut capturer des gestes effectués sur le capteur d\'empreintes digitales de l\'appareil." "Prendre une capture d\'écran" "Peut prendre une capture de l\'écran." + "Aperçu, %1$s" "désactiver ou modifier la barre d\'état" "Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système." "servir de barre d\'état" @@ -1840,6 +1841,8 @@ "Affichage plein écran" "Pour quitter, balayez vers le bas à partir du haut." "OK" + "Faire pivoter pour obtenir un meilleur affichage" + "Quitter l\'écran partagé pour obtenir un meilleur affichage" "Terminé" "Curseur circulaire des heures" "Curseur circulaire des minutes" diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 5418762932bf049ded7d081d312dd5ef460ce301..9650987af80d7a53b0c1752887bb3269b1273e0a 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -340,6 +340,7 @@ "Peut enregistrer des gestes effectués sur le lecteur d\'empreinte digitale de l\'appareil." "Prendre une capture d\'écran" "Peut prendre des captures d\'écran." + "Aperçu, %1$s" "Désactivation ou modification de la barre d\'état" "Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système." "remplacer la barre d\'état" @@ -1840,6 +1841,8 @@ "Affichage en plein écran" "Pour quitter, balayez l\'écran du haut vers le bas." "OK" + "Faites pivoter pour mieux voir" + "Quittez l\'écran partagé pour mieux voir" "OK" "Curseur circulaire des heures" "Curseur circulaire des minutes" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 8bb9b8e77673ffaf1d25b2f118ce83e9855dd2b7..dca5c25759df78f79828a10e5c126af8ac01721e 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -339,6 +339,7 @@ "Pode rexistrar os xestos realizados no sensor de impresión dixital do dispositivo." "Facer captura de pantalla" "Pode facer capturas de pantalla." + "Vista previa, %1$s" "desactivar ou modificar a barra de estado" "Permite á aplicación desactivar a barra de estado ou engadir e quitar as iconas do sistema." "actuar como a barra de estado" @@ -1839,6 +1840,8 @@ "Vendo pantalla completa" "Para saír, pasa o dedo cara abaixo desde a parte superior." "Entendido" + "Xira a pantalla para que se vexa mellor" + "Sae da pantalla dividida para que se vexa mellor" "Feito" "Control desprazable circular das horas" "Control desprazable circular dos minutos" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 34f0617af34bbeaeed80589c07c585c89ed3d923..7d02dd2bde8531c82576120bcdacfb288fba3811 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -339,6 +339,7 @@ "ડિવાઇસના ફિંગરપ્રિન્ટ સેન્સર પર કરવામાં આવેલા સંકેતો કૅપ્ચર કરી શકે છે." "સ્ક્રીનશૉટ લો" "ડિસ્પ્લેનો સ્ક્રીનશૉટ લઈ શકે છે." + "પ્રીવ્યૂ, %1$s" "સ્ટેટસ બારને અક્ષમ કરો અથવા તેમાં ફેરફાર કરો" "ઍપ્લિકેશનને સ્ટેટસ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે." "સ્ટેટસ બારમાં બતાવો" @@ -1839,6 +1840,8 @@ "પૂર્ણ સ્ક્રીન પર જુઓ" "બહાર નીકળવા માટે, ટોચ પરથી નીચે સ્વાઇપ કરો." "સમજાઈ ગયું" + "બહેતર વ્યૂ માટે ફેરવો" + "બહેતર વ્યૂ માટે, વિભાજિત સ્ક્રીનમાંથી બહાર નીકળો" "થઈ ગયું" "કલાકનું વર્તુળાકાર સ્લાઇડર" "મિનિટનું વર્તુળાકાર સ્લાઇડર" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 185b95e2e6be893df36f581ec94daf27f48edf63..695405b46863c20f5a23936104247b872072dfae 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -339,6 +339,7 @@ "डिवाइस के फ़िंगरप्रिंट सेंसर पर किए गए हाथ के जेस्चर कैप्चर किए जा सकते हैं." "स्क्रीनशॉट लें" "डिसप्ले का स्क्रीनशॉट लिया जा सकता है." + "%1$s की झलक" "स्टेटस बार को अक्षम करें या बदलें" "ऐप को, स्टेटस बार को बंद करने या सिस्‍टम आइकॉन को जोड़ने और निकालने की अनुमति देता है." "स्टेटस बार को रहने दें" @@ -1839,6 +1840,8 @@ "आप पूरे स्क्रीन पर देख रहे हैं" "बाहर निकलने के लिए, ऊपर से नीचे स्वा‍इप करें." "ठीक है" + "बेहतर व्यू पाने के लिए, डिवाइस की स्क्रीन को घुमाएं" + "बेहतर व्यू पाने के लिए, स्प्लिट स्क्रीन मोड बंद करें" "हो गया" "घंटो का चक्राकार स्लाइडर" "मिनटों का चक्राकार स्लाइडर" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index d19e963d709d06e93a1388f01ede969d0071919a..29ea8b985d7d456e709b129299534ddd3dcde529 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -340,6 +340,7 @@ "Može snimati pokrete izvršene na senzoru otiska prsta na uređaju." "Snimi zaslon" "Možete napraviti snimku zaslona." + "Pregled, %1$s" "onemogućavanje ili izmjena trake statusa" "Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona." "biti traka statusa" @@ -1840,6 +1841,8 @@ "Gledanje preko cijelog zaslona" "Za izlaz prijeđite prstom od vrha prema dolje." "Shvaćam" + "Zakrenite kako biste bolje vidjeli" + "Zatvorite podijeljeni zaslon kako biste bolje vidjeli" "Gotovo" "Kružni klizač sati" "Kružni klizač minuta" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 4e0616cc4299525dd6cc42c27b8e339e29637d86..b82064e5a349cae6fb1a7de5cf675d56b2bcc868 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -339,6 +339,7 @@ "Érzékeli az eszköz ujjlenyomat-érzékelőjén végzett kézmozdulatokat." "Képernyőkép készítése" "Készíthet képernyőképet a kijelzőről." + "Előnézet, %1$s" "állapotsor kikapcsolása vagy módosítása" "Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását." "az állapotsor szerepének átvétele" @@ -1839,6 +1840,8 @@ "Megtekintése teljes képernyőn" "Kilépéshez csúsztassa ujját fentről lefelé." "Értem" + "Forgassa el a jobb élmény érdekében" + "Lépjen ki az osztott képernyős módból a jobb élmény érdekében" "Kész" "Óra kör alakú csúszkája" "Perc kör alakú csúszkája" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 15565c069082c1e9c4c3dc4bbf19194543bd1441..8de2b7e6221ab07f68e5126393b8981ae1888f12 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -339,6 +339,7 @@ "Կարող է արձանագրել մատնահետքերի սկաների վրա կատարվող ժեստերը" "Սքրինշոթի ստեղծում" "Կարող է ստեղծել էկրանի սքրինշոթ։" + "Նախադիտում, %1$s" "անջատել կամ փոփոխել կարգավիճակի գոտին" "Թույլ է տալիս հավելվածին անջատել կարգավիճակի գոտին կամ ավելացնել ու հեռացնել համակարգի պատկերակները:" "լինել կարգավիճակի գոտի" @@ -1839,6 +1840,8 @@ "Լիաէկրան դիտում" "Դուրս գալու համար վերևից սահահարվածեք դեպի ներքև:" "Պարզ է" + "Պտտեք՝ դիտակերպը լավացնելու համար" + "Դուրս եկեք կիսված էկրանի ռեժիմից՝ դիտակերպը լավացնելու համար" "Պատրաստ է" "Ժամերի ընտրություն թվատախտակից" "Րոպեների ընտրություն թվատախտակից" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index f196186da82ea45816d8b2c7defe58f40967f67a..506fdf07a15684eb9001df893af90ae00abdfafc 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -339,6 +339,7 @@ "Dapat merekam gestur yang dilakukan di sensor sidik jari perangkat." "Ambil screenshot" "Dapat mengambil screenshot tampilan." + "Pratinjau, %1$s" "nonaktifkan atau ubah bilah status" "Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem." "jadikan bilah status" @@ -1839,6 +1840,8 @@ "Melihat layar penuh" "Untuk keluar, geser layar ke bawah dari atas." "Mengerti" + "Putar posisi layar untuk mendapatkan tampilan yang lebih baik" + "Keluar dari layar terpisah untuk mendapatkan tampilan yang lebih baik" "Selesai" "Penggeser putar jam" "Penggeser putar menit" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index b580e571fb0389da8b1481c1a0415a2bae033c11..14575cb5ce4f0db6ca324ee7c1aca3ab087bd97b 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -339,6 +339,7 @@ "Getur fangað bendingar sem eru gerðar á fingrafaralesara tækisins." "Taka skjámynd" "Getur tekið skjámynd af skjánum." + "Forskoðun, %1$s" "slökkva á eða breyta stöðustiku" "Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn." "vera stöðustikan" @@ -1839,6 +1840,8 @@ "Notar allan skjáinn" "Strjúktu niður frá efri brún til að hætta." "Ég skil" + "Snúðu til að sjá betur" + "Lokaðu skjáskiptingu til að sjá betur" "Lokið" "Valskífa fyrir klukkustundir" "Valskífa fyrir mínútur" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 6a1ee575497e60a6b7afb7341fa31f7d3825d093..7997752c684f96580ce8dac5c4c28233014a9fdf 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -340,6 +340,7 @@ "È in grado di rilevare i gesti compiuti con il sensore di impronte dei dispositivi." "Acquisire screenshot" "Può acquisire uno screenshot del display." + "%1$s in anteprima" "disattivazione o modifica della barra di stato" "Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema." "ruolo di barra di stato" @@ -1840,6 +1841,8 @@ "Visualizzazione a schermo intero" "Per uscire, scorri dall\'alto verso il basso." "OK" + "Ruota per migliorare l\'anteprima" + "Esci dallo schermo diviso per migliorare l\'anteprima" "Fine" "Cursore circolare per le ore" "Cursore circolare per i minuti" diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 84708b3df1979f053612fd9a67dc4da1b9691caa..79556938d609ac6ada395e3dd2262590ef19dc4f 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -340,6 +340,7 @@ "אפשרות לזהות תנועות בזמן נגיעה בחיישן טביעות האצבע של המכשיר." "צילום המסך" "ניתן לצלם צילום מסך של התצוגה." + "תצוגה מקדימה, %1$s" "השבתה או שינוי של שורת הסטטוס" "מאפשרת לאפליקציה להשבית את שורת הסטטוס או להוסיף ולהסיר סמלי מערכת." "להיות שורת הסטטוס" @@ -1840,6 +1841,8 @@ "צפייה במסך מלא" "כדי לצאת, פשוט מחליקים אצבע מלמעלה למטה." "הבנתי" + "מסובבים כדי לראות טוב יותר" + "צריך לצאת מהמסך המפוצל כדי לראות טוב יותר" "סיום" "מחוון שעות מעגלי" "מחוון דקות מעגלי" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index c387d4820eb95662c44e7ea2c4f3ee637ca5c419..ccf6a2dcc9663bfd9e7313d9f025fbb1da541bc3 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -339,6 +339,7 @@ "デバイスの指紋認証センサーで行われた操作をキャプチャできます。" "スクリーンショットの撮影" "ディスプレイのスクリーンショットを撮影できます。" + "プレビュー - %1$s" "ステータスバーの無効化や変更" "ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。" "ステータスバーへの表示" @@ -1839,6 +1840,8 @@ "全画面表示" "終了するには、上から下にスワイプします。" "OK" + "画面を回転させて見やすくしましょう" + "分割画面を終了して見やすくしましょう" "完了" "円形スライダー(時)" "円形スライダー(分)" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 0c8ae270db0a436dd58e3450af76774368b24308..3e695bf18f0ae5d1172c00034895a4011ffbc895 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -339,6 +339,7 @@ "შეუძლია აღბეჭდოს მოწყობილობის თითის ანაბეჭდის სენსორზე განხორციელებული ჟესტები." "ეკრანის ანაბეჭდის გადაღება" "შეუძლია ეკრანის ანაბეჭდის გადაღება." + "გადახედვა, %1$s" "სტატუსის ზოლის გათიშვა ან ცვლილება" "აპს შეეძლება სტატუსების ზოლის გათიშვა და სისტემის ხატულების დამატება/წაშლა." "სტატუსის ზოლის ჩანაცვლება" @@ -1839,6 +1840,8 @@ "სრულ ეკრანზე ნახვა" "გამოსვლისათვის, გაასრიალეთ ზემოდან ქვემოთ." "გასაგებია" + "შეატრიალეთ უკეთესი ხედისთვის" + "უკეთესი ხედვისთვის გამოდით გაყოფილი ეკრანიდან" "დასრულდა" "საათების წრიული სლაიდერი" "წუთების წრიული სლაიდერი" diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index bae2f8a20a4a855b08d8b6a2fee5d241659eb8b3..262d156a1bcf279a8f52f30e03b70242e47592c7 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -339,6 +339,7 @@ "Құрылғының саусақ ізі сенсорында орындалған қимылдарды сақтайды." "Скриншот жасау" "Дисплейдің скриншотын жасай аласыз." + "Алғы көрініс, %1$s" "күйін көрсету тақтасын өшіру немесе өзгерту" "Қолданбаға күй жолағын өшіруге немесе жүйелік белгішелерді қосуға және жоюға рұқсат береді." "күй жолағы болу" @@ -1839,6 +1840,8 @@ "Толық экранда көру" "Шығу үшін жоғарыдан төмен қарай сырғытыңыз." "Түсінікті" + "Жақсырақ көру үшін бұрыңыз." + "Жақсырақ көру үшін экранды бөлу режимінен шығыңыз." "Дайын" "Сағаттар айналымының қозғалтқышы" "Минут айналымын қозғалтқыш" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 8a00dbc63e72183b7b4245c6a5b63008abdde48b..191ddf70bdb88ca2f58a6f55dfd4c22d7f5c6de5 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -339,6 +339,7 @@ "អាចចាប់យក​ចលនា​ដែលធ្វើនៅលើ​នៅលើ​ឧបករណ៍​ចាប់​ស្នាម​ម្រាមដៃ​របស់ឧបករណ៍បាន។" "ថត​អេក្រង់" "អាច​ថត​អេក្រង់​នៃ​ផ្ទាំង​អេក្រង់​បាន។" + "មើល​សាកល្បង %1$s" "បិទ ឬ​កែ​របារ​ស្ថានភាព" "ឲ្យ​កម្មវិធី​បិទ​របារ​ស្ថានភាព ឬ​បន្ថែម និង​លុប​រូប​តំណាង​ប្រព័ន្ធ។" "ធ្វើជារបារស្ថានភាព" @@ -1839,6 +1840,8 @@ "កំពុងមើលពេញអេក្រង់" "ដើម្បីចាកចេញ សូមអូសពីលើចុះក្រោម។" "យល់ហើយ" + "បង្វិលដើម្បីមើលបានកាន់តែច្បាស់" + "ចេញពីមុខងារ​បំបែកអេក្រង់ដើម្បីមើលបានកាន់តែច្បាស់" "រួចរាល់" "គ្រាប់​រំកិល​រង្វង់​ម៉ោង" "គ្រាប់​រំកិល​រង្វង់​នាទី" diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index ec018026a38eed9f4cd73edbab4b4ec96bc7b407..084a8fe85ee573b820f62f61fd3281a4d0144421 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -339,6 +339,7 @@ "ಸಾಧನದ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌ನಲ್ಲಿ ನಡೆಸಿದ ಗೆಶ್ಚರ್‌ಗಳನ್ನು ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿ." "ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ತೆಗೆದುಕೊಳ್ಳಿ" "ಪ್ರದರ್ಶನದ ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಲ್ಲದು." + "ಪೂರ್ವವೀಕ್ಷಣೆ, %1$s" "ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ" "ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ." "ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು" @@ -1839,6 +1840,8 @@ "ಪೂರ್ಣ ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ" "ನಿರ್ಗಮಿಸಲು, ಮೇಲಿನಿಂದ ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ." "ತಿಳಿಯಿತು" + "ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ತಿರುಗಿಸಿ" + "ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ನಿಂದ ನಿರ್ಗಮಿಸಿ" "ಮುಗಿದಿದೆ" "ಗಂಟೆಗಳ ವೃತ್ತಾಕಾರ ಸ್ಲೈಡರ್" "ನಿಮಿಷಗಳ ವೃತ್ತಾಕಾರ ಸ್ಲೈಡರ್" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 2ae119d25911a8326988eceeaf923e53c42d5012..d93784b832194f0d6235cc59bcaa372bf9c795d0 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -339,6 +339,7 @@ "기기 지문 센서에서 동작을 캡처합니다." "스크린샷 촬영" "디스플레이 스크린샷을 촬영할 수 있습니다." + "미리보기, %1$s" "상태 표시줄 사용 중지 또는 수정" "앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다." "상태 표시줄에 위치" @@ -1839,6 +1840,8 @@ "전체 화면 모드" "종료하려면 위에서 아래로 스와이프합니다." "확인" + "카메라 미리보기 화면이 잘 보이도록 회전하세요." + "카메라 미리보기 화면이 잘 보이도록 화면 분할을 종료하세요." "완료" "시간 원형 슬라이더" "분 원형 슬라이더" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 5bf3dfce5666dba770feac63c0d62c920d6d6ed9..1b307f1d2acaad5aaeccc8857571700522b60f3a 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -339,6 +339,7 @@ "Түзмөктөгү манжа изинин сенсорунда жасалган жаңсоолорду жаздырып алат." "Скриншот тартып алуу" "Дисплейдин скриншотун тартып алсаңыз болот." + "Алдын ала көрүү, %1$s" "абал тилкесин өчүрүү же өзгөртүү" "Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет." "абал тилкесинин милдетин аткаруу" @@ -1839,6 +1840,8 @@ "Толук экран режими" "Чыгуу үчүн экранды ылдый сүрүп коюңуз." "Түшүндүм" + "Жакшыраак көрүү үчүн буруңуз" + "Жакшыраак көрүү үчүн экранды бөлүү режиминен чыгыңыз" "Даяр" "Саат жебеси" "Мүнөт жебеси" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 40951c27f6d8ed80362617eb06b5cf0e29911282..25352c09dde78d6883306c0b5bbfc98ad8cf97af 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -339,6 +339,7 @@ "ສາມາດບັນທຶກທ່າທາງທີ່ເກີດຂຶ້ນໃນອຸປະກອນເຊັນເຊີລາຍນິ້ວມືໄດ້." "ຖ່າຍຮູບໜ້າຈໍ" "ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້." + "ຕົວຢ່າງ, %1$s" "ປິດການນນຳໃຊ້ ຫຼື ແກ້ໄຂແຖບສະຖານະ" "ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງແຖບສະຖານະ ຫຼືເພີ່ມ ແລະລຶບໄອຄອນລະບົບອອກໄດ້." "ເປັນ​ແຖບ​ສະ​ຖາ​ນະ" @@ -1839,6 +1840,8 @@ "ການ​ເບິ່ງ​ເຕັມ​ໜ້າ​ຈໍ" "ຫາກຕ້ອງການອອກ, ໃຫ້ຮູດຈາກທາງເທິງລົງມາທາງລຸ່ມ." "ໄດ້​ແລ້ວ" + "ໝຸນເພື່ອມຸມມອງທີ່ດີຂຶ້ນ" + "ອອກຈາກແບ່ງໜ້າຈໍເພື່ອມຸມມອງທີ່ດີຂຶ້ນ" "ແລ້ວໆ" "ໂຕໝຸນປັບຊົ່ວໂມງ" "ໂຕໝຸນປັບນາທີ" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 58a17c8c0171b7b72f23dafb33f91ebe2863a701..56aca55e1111a1854fa3fa6cbe312c32334c03dc 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -341,6 +341,7 @@ "Gali užfiksuoti gestus, atliktus naudojant įrenginio piršto antspaudo jutiklį." "Ekrano kopijos kūrimas" "Galima sukurti vaizdo ekrano kopiją." + "Peržiūra, %1$s" "išjungti ar keisti būsenos juostą" "Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas." "būti būsenos juosta" @@ -1841,6 +1842,8 @@ "Peržiūrima viso ekrano režimu" "Jei norite išeiti, perbraukite žemyn iš viršaus." "Supratau" + "Pasukite, kad geriau matytumėte vaizdą" + "Išeikite iš išskaidyto ekrano režimo, kad geriau matytumėte vaizdą" "Atlikta" "Apskritas valandų šliaužiklis" "Apskritas minučių šliaužiklis" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index ae60301c89d42f18cf78de8693849bc4bb44c028..1f1cd674f65c38c3704860e24da100376272e89f 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -340,6 +340,7 @@ "Var uztvert žestus ierīces pirksta nospieduma sensorā." "Ekrānuzņēmuma izveide" "Var izveidot displeja ekrānuzņēmumu." + "%1$s (priekšskatījums)" "atspējot vai pārveidot statusa joslu" "Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas." "Būt par statusa joslu" @@ -1840,6 +1841,8 @@ "Skatīšanās pilnekrāna režīmā" "Lai izietu, no augšdaļas velciet lejup." "Labi" + "Lai uzlabotu skatu, pagrieziet ekrānu." + "Lai uzlabotu skatu, izejiet no ekrāna sadalīšanas režīma." "Gatavs" "Stundu apļveida slīdnis" "Minūšu apļveida slīdnis" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index d5629d5c6aa6b355751a3ae31b055c721f77f2bc..a6ab8603fd5b83bd84ebc8e8047762445e6cc22b 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -339,6 +339,7 @@ "Може да сними движења што се направени на сензорот за отпечатоци на уредот." "Зачувување слика од екранот" "Може да зачува слика од екранот." + "Преглед, %1$s" "оневозможи или измени статусна лента" "Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони." "да стане статусна лента" @@ -1839,6 +1840,8 @@ "Се прикажува на цел екран" "За да излезете, повлечете одозгора надолу." "Сфатив" + "Ротирајте за подобар приказ" + "За подобар приказ, излезете од поделениот екран" "Готово" "Приказ на часови во кружно движење" "Приказ на минути во кружно движење" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 3e5933478e71b336756ab1ec54f4d87b5aef53eb..faac4b3e4ced7b249d0f617ff8cd6c15efd3648e 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -339,6 +339,7 @@ "ഉപകരണത്തിന്റെ ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെയ്‌ത ജെസ്‌റ്ററുകൾ ക്യാപ്‌ചർ ചെയ്യാനാകും." "സ്ക്രീന്‍ഷോട്ട് എടുക്കുക" "ഡിസ്‌പ്ലേയുടെ സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ കഴിയും." + "പ്രിവ്യൂ, %1$s" "സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്‌ക്കരിക്കുക" "നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്‌റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു." "സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക" @@ -1839,6 +1840,8 @@ "പൂർണ്ണ സ്‌ക്രീനിൽ കാണുന്നു" "അവസാനിപ്പിക്കാൻ, മുകളിൽ നിന്ന് താഴോട്ട് സ്വൈപ്പ് ചെയ്യുക." "മനസ്സിലായി" + "മികച്ച കാഴ്‌ചയ്‌ക്കായി റൊട്ടേറ്റ് ചെയ്യുക" + "മികച്ച കാഴ്‌ചയ്‌ക്കായി സ്‌ക്രീൻ വിഭജന മോഡിൽ നിന്ന് പുറത്തുകടക്കുക" "പൂർത്തിയാക്കി" "ചാക്രികമായി മണിക്കൂറുകൾ ദൃശ്യമാകുന്ന സ്ലൈഡർ" "ചാക്രികമായി മിനിറ്റുകൾ ദൃശ്യമാകുന്ന സ്ലൈഡർ" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index c9eb10058857c07530765fdca40b20b61eee2a00..99933719f040a0cbd31d32dc5b155c96100eed85 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -339,6 +339,7 @@ "Төхөөрөмжийн хурууны хээ мэдрэгчид зангасан зангааг танина." "Дэлгэцийн зургийг дарах" "Дэлгэцийн зургийг дарах боломжтой." + "Урьдчилан үзэх, %1$s" "статус самбарыг идэвхгүй болгох болон өөрчлөх" "Апп нь статус самбарыг идэвхгүй болгох эсвэл систем дүрсийг нэмэх, хасах боломжтой." "статусын хэсэг болох" @@ -1839,6 +1840,8 @@ "Бүтэн дэлгэцээр үзэж байна" "Гарахаар бол дээрээс нь доош нь чирнэ үү." "Ойлголоо" + "Харагдах байдлыг сайжруулах бол эргүүлнэ үү" + "Харагдах байдлыг сайжруулах бол дэлгэцийг хуваах горимоос гарна уу" "Дууссан" "Цаг гүйлгэгч" "Минут гүйлгэгч" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 45e7f1ccac79a64f380dd12add5d0ed67e834925..30f9d05aab6a0e983aa7634d158bbd6559e78645 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -339,6 +339,7 @@ "डिव्‍हाइसच्‍या फिंगरप्रिंट सेंन्सरवरील जेश्चर कॅप्‍चर करू शकते." "स्क्रीनशॉट घ्या" "डिस्प्लेचा स्क्रीनशॉट घेऊ शकतो." + "पूर्वावलोकन, %1$s" "स्टेटस बार अक्षम करा किंवा सुधारित करा" "स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अ‍ॅप ला अनुमती देते." "स्टेटस बार होऊ द्या" @@ -1839,6 +1840,8 @@ "पूर्ण स्क्रीनवर पाहत आहात" "बाहेर पडण्यासाठी, वरून खाली स्वाइप करा." "समजले" + "अधिक चांगल्या दृश्यासाठी फिरवा" + "अधिक चांगल्या दृश्यासाठी स्प्लिट स्क्रीनमधून बाहेर पडा" "पूर्ण झाले" "तास परिपत्रक स्लायडर" "मिनिटे परिपत्रक स्लायडर" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index c17cb657b6a8818f9b73b4b78cbba42281d2e59b..80c8ae04f526c54db6b4eb2c17d91eea4ea564f2 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -339,6 +339,7 @@ "Boleh menangkap gerak isyarat yang dilakukan pada penderia cap jari peranti." "Ambil tangkapan skrin" "Boleh mengambil tangkapan skrin paparan." + "Pratonton, %1$s" "lumpuhkan atau ubah suai bar status" "Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem." "jadi bar status" @@ -1839,6 +1840,8 @@ "Melihat skrin penuh" "Untuk keluar, leret dari atas ke bawah." "Faham" + "Putar untuk mendapatkan paparan yang lebih baik" + "Keluar daripada skrin pisah untuk mendapatkan paparan yang lebih baik" "Selesai" "Penggelangsar bulatan jam" "Penggelangsar bulatan minit" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index c8198603ed8f996b6f0546f23491b29f9deccfce..97267ef1c9619f588ffc87ba840a3d145a1c46af 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -339,6 +339,7 @@ "စက်ပစ္စည်း၏ လက်ဗွေအာရုံခံကိရိယာတွင် လုပ်ဆောင်ထားသည့် လက်ဟန်များကို မှတ်သားထားနိုင်သည်။" "ဖန်သားပြင်ဓာတ်ပုံ ရိုက်ရန်" "ဖန်သားပြင်ပြသမှုကို ဓာတ်ပုံရိုက်နိုင်ပါသည်။" + "%1$s အစမ်းကြည့်ခြင်း" "အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်" "အက်ပ်အား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။" "အခြေအနေပြ ဘားဖြစ်ပါစေ" @@ -1839,6 +1840,8 @@ "မျက်နှာပြင်အပြည့် ကြည့်နေသည်" "ထွက်ရန် အပေါ်မှ အောက်သို့ ဆွဲချပါ။" "ရပါပြီ" + "ပိုကောင်းသောမြင်ကွင်းအတွက် လှည့်ပါ" + "ပိုကောင်းသောမြင်ကွင်းအတွက် မျက်နှာပြင် ခွဲ၍ပြသခြင်းမှ ထွက်ပါ" "ပြီးပါပြီ" "နာရီရွေးချက်စရာ" "မိနစ်လှည့်သော ရွေ့လျားတန်" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 2de0c4b9bbb3667674915dcc67b8847d4103e197..549ee0c381bfb89c2f5745b5b29a2caa97e3a502 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -339,6 +339,7 @@ "Kan fange inn bevegelser som utføres på enhetens fingeravtrykkssensor." "Ta skjermdump" "Kan ikke ta en skjermdump av skjermen." + "Forhåndsvisning, %1$s" "deaktivere eller endre statusfeltet" "Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner." "vise appen i statusfeltet" @@ -1839,6 +1840,8 @@ "Visning i fullskjerm" "Sveip ned fra toppen for å avslutte." "Skjønner" + "Roter for å få en bedre visning" + "Avslutt delt skjerm for å få en bedre visning" "Ferdig" "Sirkulær glidebryter for timer" "Sirkulær glidebryter for minutter" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index f9333a0105344f7c97f55058bb4dda0a686b3de6..9a414ccb48a87c2eec64d27524b08302af70aa65 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -339,6 +339,7 @@ "यसले यन्त्रक‍ो फिंगरप्रिन्टसम्बन्धी सेन्सरमा गरिएका इसाराहरूलाई खिच्‍न सक्छ।" "स्क्रिनसट लिनुहोस्" "डिस्प्लेको स्क्रिनसट लिन सकिन्छ।" + "प्रिभ्यू, %1$s" "स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्" "स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन एपलाई अनुमति दिन्छ।" "स्टाटस बार हुन दिनुहोस्" @@ -1839,6 +1840,8 @@ "पूरा पर्दा हेर्दै" "बाहिर निस्कन, माथिबाट तल स्वाइप गर्नुहोस्।" "बुझेँ" + "अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने रोटेट गर्नुहोस्" + "अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने \"स्प्लिट स्क्रिन\" बाट बाहिरिनुहोस्" "भयो" "घन्टा गोलाकार स्लाइडर" "मिनेट गोलाकार स्लाइडर" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 2fe7ebc1ff0dc697e9ea961648f435f6e4cda529..801676131968dbb2c83af82d313de3f7b1b67737 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -339,6 +339,7 @@ "Kan gebaren registreren die op de vingerafdruksensor van het apparaat worden getekend." "Screenshot maken" "Kan een screenshot van het scherm maken." + "Voorbeeld, %1$s" "statusbalk uitzetten of wijzigen" "Hiermee kan de app de statusbalk uitzetten of systeemiconen toevoegen en verwijderen." "de statusbalk zijn" @@ -1839,6 +1840,8 @@ "Volledig scherm wordt getoond" "Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten." "Ik snap het" + "Draai voor een betere weergave" + "Sluit het gesplitste scherm voor een betere weergave" "Klaar" "Ronde schuifregelaar voor uren" "Ronde schuifregelaar voor minuten" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index e958bb8f9fd8519a1d9e2c625d8e30bb6689a0c0..f9502cbd8b3dbeb94574a0425ea140f0691d2633 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -339,6 +339,7 @@ "ଡିଭାଇସ୍‌ର ଟିପଚିହ୍ନ ସେନସର୍ ଉପରେ ଜେଶ୍ଚର୍‍ କ୍ୟାପଚର୍‍ କାର୍ଯ୍ୟ କରାଯାଇପାରିବ।" "ସ୍କ୍ରିନସଟ୍ ନିଅନ୍ତୁ" "ଡିସପ୍ଲେର ଏକ ସ୍କ୍ରିନସଟ୍ ନିଆଯାଇପାରେ।" + "ପ୍ରିଭ୍ୟୁ, %1$s" "ଷ୍ଟାଟସ୍‌ ବାର୍‌କୁ ଅକ୍ଷମ କିମ୍ୱା ସଂଶୋଧନ କରନ୍ତୁ" "ଆପ୍‍କୁ, ସ୍ଥିତି ବାର୍‍ ଅକ୍ଷମ କରିବାକୁ କିମ୍ବା ସିଷ୍ଟମ୍‍ ଆଇକନ୍‍ ଯୋଡ଼ିବା କିମ୍ବା ବାହାର କରିବାକୁ ଦେଇଥାଏ।" "ଷ୍ଟାଟସ୍‍ ବାର୍‍ ରହିବାକୁ ଦିଅନ୍ତୁ" @@ -1600,7 +1601,7 @@ "ଆଙ୍ଗୁଠି ଚିହ୍ନ:" "SHA-256 ଆଙ୍ଗୁଠି ଚିହ୍ନ:" "SHA-1 ଟିପଚିହ୍ନ:" - "ସମସ୍ତ ଦେଖନ୍ତୁ" + "ସବୁ ଦେଖନ୍ତୁ" "ଗତିବିଧି ଚୟନ କରନ୍ତୁ" "ଏହାଙ୍କ ସହ ସେୟାର୍‍ କରନ୍ତୁ" "ପଠାଯାଉଛି…" @@ -1839,6 +1840,8 @@ "ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ଦେଖାଯାଉଛି" "ବାହାରିବା ପାଇଁ, ଉପରୁ ତଳକୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ।" "ବୁଝିଗଲି" + "ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ରୋଟେଟ କରନ୍ତୁ" + "ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନରୁ ବାହାରି ଯାଆନ୍ତୁ" "ହୋଇଗଲା" "ଘଣ୍ଟା ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍" "ମିନିଟ୍ସ ସର୍କୁଲାର୍‍ ସ୍ଲାଇଡର୍‍" diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 5b2c186d71d16c777e3dcdf98e020eeeb5f18e9f..3967c264c13d874175933f6519f08dcd151d9b55 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -339,6 +339,7 @@ "ਡੀਵਾਈਸਾਂ ਦੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ \'ਤੇ ਕੀਤੇ ਗਏ ਸੰਕੇਤਾਂ ਨੂੰ ਕੈਪਚਰ ਕਰ ਸਕਦੇ ਹਨ।" "ਸਕ੍ਰੀਨਸ਼ਾਟ ਲਓ" "ਡਿਸਪਲੇ ਦਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈ ਸਕਦੀ ਹੈ।" + "ਪੂਰਵ-ਝਲਕ ਦੇਖੋ, %1$s" "ਸਥਿਤੀ ਪੱਟੀ ਬੰਦ ਕਰੋ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ" "ਐਪ ਨੂੰ ਸਥਿਤੀ ਪੱਟੀ ਨੂੰ ਚਾਲੂ ਕਰਨ ਜਾਂ ਸਿਸਟਮ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਜੋੜਨ ਅਤੇ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।" "ਸਥਿਤੀ ਪੱਟੀ ਬਣਨ ਦਿਓ" @@ -1839,6 +1840,8 @@ "ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦੇਖੋ" "ਬਾਹਰ ਜਾਣ ਲਈ, ਉਪਰੋਂ ਹੇਠਾਂ ਸਵਾਈਪ ਕਰੋ।" "ਸਮਝ ਲਿਆ" + "ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ ਘੁਮਾਓ" + "ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਤੋਂ ਬਾਹਰ ਆਓ" "ਹੋ ਗਿਆ" "ਘੰਟੇ ਸਰਕੁਲਰ ਸਲਾਈਡਰ" "ਮਿੰਟ ਸਰਕੁਲਰ ਸਲਾਈਡਰ" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 7a08a3edaee71e35104865e69f4e1f15d496fc3f..cb5361dbc60fa0377e659c9e274329ac0cbe9b38 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -341,6 +341,7 @@ "Może przechwytywać gesty wykonywane na czytniku linii papilarnych w urządzeniu." "Robienie zrzutu ekranu" "Może robić zrzuty ekranu wyświetlacza." + "Podgląd, %1$s" "wyłączanie lub zmienianie paska stanu" "Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych." "działanie jako pasek stanu" @@ -1841,6 +1842,8 @@ "Włączony pełny ekran" "Aby wyjść, przesuń palcem z góry na dół." "OK" + "Obróć, aby lepiej widzieć" + "Zamknij podzielony ekran, aby lepiej widzieć" "Gotowe" "Kołowy suwak godzin" "Kołowy suwak minut" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 2a72af1bee93691a8cc4305dbf829a35f18d8654..aa11e2b86e779b9b8b7d13be4bdf27ec5b58bece 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -340,6 +340,7 @@ "Pode captar gestos realizados no sensor de impressão digital do dispositivo." "Fazer uma captura de tela" "Pode fazer uma captura de tela." + "Visualização, %1$s" "desativar ou modificar a barra de status" "Permite que o app desative a barra de status ou adicione e remova ícones do sistema." "ser a barra de status" @@ -1840,6 +1841,8 @@ "Visualização em tela cheia" "Para sair, deslize de cima para baixo." "Entendi" + "Gire a tela para ter uma visualização melhor" + "Saia da tela dividida para ter uma visualização melhor" "Concluído" "Controle deslizante circular das horas" "Controle deslizante circular dos minutos" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 4772d11986280075e5398afcb3c1023549fe18e1..bc771cdc8bbd4ac9fb1002007f738049403ce430 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -340,6 +340,7 @@ "Pode capturar gestos realizados no sensor de impressões digitais do dispositivo." "Fazer captura de ecrã" "É possível tirar uma captura de ecrã." + "Pré-visualização, %1$s" "desativar ou modificar barra de estado" "Permite à app desativar a barra de estado ou adicionar e remover ícones do sistema." "ser apresentada na barra de estado" @@ -1840,6 +1841,8 @@ "Visualização de ecrã inteiro" "Para sair, deslize rapidamente para baixo a partir da parte superior." "OK" + "Rode para uma melhor visualização" + "Saia do ecrã dividido para uma melhor visualização" "Concluído" "Controlo de deslize circular das horas" "Controlo de deslize circular dos minutos" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 2a72af1bee93691a8cc4305dbf829a35f18d8654..aa11e2b86e779b9b8b7d13be4bdf27ec5b58bece 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -340,6 +340,7 @@ "Pode captar gestos realizados no sensor de impressão digital do dispositivo." "Fazer uma captura de tela" "Pode fazer uma captura de tela." + "Visualização, %1$s" "desativar ou modificar a barra de status" "Permite que o app desative a barra de status ou adicione e remova ícones do sistema." "ser a barra de status" @@ -1840,6 +1841,8 @@ "Visualização em tela cheia" "Para sair, deslize de cima para baixo." "Entendi" + "Gire a tela para ter uma visualização melhor" + "Saia da tela dividida para ter uma visualização melhor" "Concluído" "Controle deslizante circular das horas" "Controle deslizante circular dos minutos" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 03ec760141a547b7d3164bf4b4d2ae8dc6d554bd..d7d6ef3ecf42fdcc71005cee7ac7d5b7dd7095ff 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -340,6 +340,7 @@ "Poate reda gesturile făcute pe senzorul de amprentă al dispozitivului." "Fă o captură de ecran" "Poate face o captură de ecran." + "Previzualizare, %1$s" "dezactivare sau modificare bare de stare" "Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem." "să fie bara de stare" @@ -1840,6 +1841,8 @@ "Vizualizare pe ecran complet" "Pentru a ieși, glisează de sus în jos." "Am înțeles" + "Rotește pentru o previzualizare mai bună" + "Ieși din ecranul împărțit pentru o previzualizare mai bună" "Terminat" "Selector circular pentru ore" "Selector circular pentru minute" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index bb081a0406f06c4f2a43eb1efd5f27b4de6871ad..5ddac36f115bde84097e4b93686bd77cfd338668 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -341,6 +341,7 @@ "Использовать сканер отпечатков пальцев для дополнительных жестов." "Создавать скриншоты" "Создавать снимки экрана." + "%1$s: предпросмотр" "Отключение/изменение строки состояния" "Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки." "Замена строки состояния" @@ -1841,6 +1842,8 @@ "Полноэкранный режим" "Чтобы выйти, проведите по экрану сверху вниз." "ОК" + "Поверните, чтобы лучше видеть." + "Выйдите из режима разделения экрана, чтобы лучше видеть." "Готово" "Выбор часов на циферблате" "Выбор минут на циферблате" diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 3f62b9c387c368dfd45c0e714ab719cd2a7c05dd..196d57ac742e7c2e35dfc69d7cdef456afebf80d 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -339,6 +339,7 @@ "උපාංගයෙහි ඇඟිලි සලකුණු සංවේදකය මත සිදු කරන ඉංගිත ග්‍රහණය කළ හැකිය." "තිර රුව ගන්න" "සංදර්ශකයේ තිර රුවක් ගැනීමට හැකිය." + "පෙරදසුන, %1$s" "තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න" "තත්ව තීරුව අක්‍රිය කිරීමට හෝ පද්ධති නිරූපක එකතු හෝ ඉවත් කිරීමට යෙදුමට අවසර දේ." "තත්ත්ව තීරුව බවට පත්වීම" @@ -1839,6 +1840,8 @@ "මුළු තිරය බලමින්" "ඉවත් වීමට, ඉහළ සිට පහළට ස්වයිප් කරන්න" "වැටහුණි" + "වඩා හොඳ දසුනක් සඳහා කරකවන්න" + "වඩා හොඳ දර්ශනයක් සඳහා බෙදුම් තිරයෙන් පිටවන්න" "අවසන්" "පැය කවාකාර සර්පනය" "මිනිත්තු කවාකාර සර්පනය" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index c513f9cfa63a3157649a4c11f18f0d9ad7b6edeb..66ac4728abb680b65cce41dfaad89f37fa1c8c9f 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -341,6 +341,7 @@ "Dokáže zaznamenať gestá na senzore odtlačkov prstov zariadenia." "Vytvoriť snímku obrazovky" "Je možné vytvoriť snímku obrazovky." + "Ukážka, %1$s" "zakázanie alebo zmeny stavového riadka" "Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony." "vydávanie sa za stavový riadok" @@ -1841,6 +1842,8 @@ "Zobrazenie na celú obrazovku" "Ukončíte potiahnutím zhora nadol." "Dobre" + "Otočte zariadenie pre lepšie zobrazenie" + "Ukončite rozdelenú obrazovku pre lepšie zobrazenie" "Hotovo" "Kruhový posúvač hodín" "Kruhový posúvač minút" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 78e3bf3e487393dae5a5734c85866c4e4bf53fbc..b3d5f6d4ee02ce96efbe8070be93636631a9a9ad 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -341,6 +341,7 @@ "Prepoznava poteze, narejene po tipalu prstnih odtisov naprave." "Ustvarjanje posnetka zaslona" "Lahko naredi posnetek zaslona." + "Predogled, %1$s" "onemogočanje ali spreminjanje vrstice stanja" "Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema." "postane vrstica stanja" @@ -1841,6 +1842,8 @@ "Vklopljen je celozaslonski način" "Zaprete ga tako, da z vrha s prstom povlečete navzdol." "Razumem" + "Zasukajte za boljši pregled." + "Zaprite razdeljeni zaslon za boljši pregled." "Dokončano" "Okrogli drsnik za ure" "Okrogli drsnik za minute" diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 8aff5dbb4b11fbcd62da27a2511d8e79742bb6e7..270ae48db367c428da1d10a3b06e2c10f570125e 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -339,6 +339,7 @@ "Mund të regjistrojë gjestet e kryera në sensorin e gjurmës së gishtit të pajisjes." "Nxirr një pamje të ekranit" "Mund të nxjerrë një pamje e ekranit." + "Versioni paraprak, %1$s" "çaktivizo ose modifiko shiritin e statusit" "Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit." "të bëhet shiriti i statusit" @@ -1839,6 +1840,8 @@ "Po shikon ekranin e plotë" "Për të dalë, rrëshqit nga lart poshtë." "E kuptova" + "Rrotullo për një pamje më të mirë" + "Dil nga ekrani i ndarë për një pamje më të mirë" "U krye" "Rrëshqitësi rrethor i orëve" "Rrëshqitësi rrethor i minutave" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 47db1e3e2aaf6050132f351f14af8d6c41174287..37727cf44dc6d4748811efafade0d09896eea1b2 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -340,6 +340,7 @@ "Може да региструје покрете на сензору за отисак прста на уређају." "Направи снимак екрана" "Може да направи снимак екрана." + "Преглед, %1$s" "онемогућавање или измена статусне траке" "Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе." "функционисање као статусна трака" @@ -1840,6 +1841,8 @@ "Приказује се цео екран" "Да бисте изашли, превуците надоле одозго." "Важи" + "Ротирајте ради бољег приказа" + "Изађите из подељеног екрана ради бољег приказа" "Готово" "Кружни клизач за сате" "Кружни клизач за минуте" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 5acbfba28e20420246e73d3c8e5c72329d19a7d9..1cb246a4ceccf99053174168a20a5b03e1259354 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -339,6 +339,7 @@ "Kan registrera rörelser som utförs med hjälp av enhetens fingeravtryckssensor." "Ta skärmbild" "Kan ta en skärmbild av skärmen." + "Förhandsgranskar %1$s" "inaktivera eller ändra statusfält" "Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner." "visas i statusfältet" @@ -1839,6 +1840,8 @@ "Visar på fullskärm" "Svep nedåt från skärmens överkant för att avsluta." "OK" + "Rotera för att få en bättre vy" + "Stäng delad skärm för att få en bättre vy" "Klart" "Cirkelreglage för timmar" "Cirkelreglage för minuter" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 046ccf181568f859c3c5dfe0e77a4fd9927efadf..35d233af6ab9b63e10b57863b6f8f1367d03f0dd 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -339,6 +339,7 @@ "Inaweza kurekodi ishara zinazotekelezwa kwenye kitambua alama ya kidole." "Piga picha ya skrini" "Inaweza kupiga picha ya skrini ya onyesho." + "Onyesho la kukagua, %1$s" "zima au rekebisha mwambaa hali" "Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa aikoni za mfumo." "kuwa sehemu ya arifa" @@ -1839,6 +1840,8 @@ "Unatazama skrini nzima" "Ili kuondoka, telezesha kidole kutoka juu hadi chini." "Nimeelewa" + "Zungusha ili upate mwonekano bora" + "Funga skrini iliyogawanywa ili upate mwonekano bora" "Imekamilika" "Kitelezi cha mviringo wa saa" "Kitelezi cha mviringo wa dakika" diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index dcc1c5a2ed3f7f99121c66f2fd326488da3703b0..9a99d560d0004bb6500840f517bd3100755372fd 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -339,6 +339,7 @@ "சாதனத்தின் கைரேகை சென்சார்மேல் செய்யப்படும் சைகைகளைக் கேப்ட்சர் செய்ய முடியும்." "ஸ்கிரீன்ஷாட்டை எடுக்கும்" "டிஸ்ப்ளேவை ஸ்கிரீன்ஷாட் எடுக்க முடியும்." + "மாதிரிக்காட்சி, %1$s" "நிலைப் பட்டியை முடக்குதல் அல்லது மாற்றுதல்" "நிலைப் பட்டியை முடக்க அல்லது முறைமையில் ஐகான்களைச் சேர்க்க மற்றும் அகற்ற ஆப்ஸை அனுமதிக்கிறது." "நிலைப் பட்டியில் இருக்கும்" @@ -1839,6 +1840,8 @@ "முழுத் திரையில் காட்டுகிறது" "வெளியேற, மேலிருந்து கீழே ஸ்வைப் செய்யவும்" "புரிந்தது" + "சிறந்த காட்சிக்கு சுழற்றுங்கள்" + "சிறந்த காட்சிக்கு திரைப் பிரிப்புப் பயன்முறையில் இருந்து வெளியேறுங்கள்" "முடிந்தது" "மணிநேர வட்ட வடிவ ஸ்லைடர்" "நிமிடங்களுக்கான வட்டவடிவ ஸ்லைடர்" diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 6a374fd916051d5bf436665e9a24de6a0adc857f..5beee424a5a7a3421e861082e0fddc303367c872 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -339,6 +339,7 @@ "పరికర వేలిముద్ర సెన్సార్‌లో ఉపయోగించిన సంజ్ఞలను క్యాప్చర్ చేయవచ్చు." "స్క్రీన్‌షాట్‌ను తీయండి" "డిస్‌ప్లే యొక్క స్క్రీన్‌షాట్ తీసుకోవచ్చు." + "ప్రివ్యూ, %1$s" "స్టేటస్‌ బార్‌ను డిజేబుల్ చేయడం లేదా మార్చడం" "స్టేటస్‌ బార్‌ను డిజేబుల్ చేయడానికి లేదా సిస్టమ్ చిహ్నాలను జోడించడానికి మరియు తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది." "స్టేటస్‌ పట్టీగా ఉండటం" @@ -1839,6 +1840,8 @@ "ఫుల్-స్క్రీన్‌లో వీక్షిస్తున్నారు" "నిష్క్రమించడానికి, పై నుండి క్రిందికి స్వైప్ చేయండి." "అర్థమైంది" + "మెరుగైన వీక్షణ కోసం తిప్పండి" + "మెరుగైన వీక్షణ కోసం స్ప్లిట్ స్క్రీన్ నుండి నిష్క్రమించండి" "పూర్తయింది" "గంటల వృత్తాకార స్లయిడర్" "నిమిషాల వృత్తాకార స్లయిడర్" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 9c46a0aa0133f907f74e9be2bd27211cb4cd77aa..c22f19c5d55b253e615c04cc89caf3f856eb983c 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -339,6 +339,7 @@ "สามารถจับท่าทางสัมผัสที่เกิดขึ้นบนเซ็นเซอร์ลายนิ้วมือของอุปกรณ์" "ถ่ายภาพหน้าจอ" "ถ่ายภาพหน้าจอได้" + "ตัวอย่าง %1$s" "ปิดการใช้งานหรือแก้ไขแถบสถานะ" "อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก" "เป็นแถบสถานะ" @@ -1839,6 +1840,8 @@ "กำลังดูแบบเต็มหน้าจอ" "หากต้องการออก ให้เลื่อนลงจากด้านบน" "รับทราบ" + "หมุนเพื่อรับมุมมองที่ดียิ่งขึ้น" + "ออกจากโหมดแยกหน้าจอเพื่อรับมุมมองที่ดียิ่งขึ้น" "เสร็จสิ้น" "ตัวเลื่อนหมุนระบุชั่วโมง" "ตัวเลื่อนหมุนระบุนาที" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index b3748f64f51bdfef446c7e60ee2a6a4d64875c5d..b0f85b09bf1dce701159585a3c23e09e55ac69e1 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -339,6 +339,7 @@ "Makukunan ang mga galaw na ginawa sa sensor para sa fingerprint ng device." "Kumuha ng screenshot" "Puwedeng kumuha ng screenshot ng display." + "Preview, %1$s" "i-disable o baguhin ang status bar" "Pinapayagan ang app na i-disable ang status bar o magdagdag at mag-alis ng mga icon ng system." "maging status bar" @@ -1839,6 +1840,8 @@ "Panonood sa full screen" "Upang lumabas, mag-swipe mula sa itaas pababa." "Nakuha ko" + "I-rotate para sa mas magandang view" + "Lumabas sa split screen para sa mas magandang view" "Tapos na" "Pabilog na slider ng mga oras" "Pabilog na slider ng mga minuto" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 816f89ab4c94896f5e3267efec3116d4207d437a..56e8f9605ab448bcf19bd6385224285ee2568860 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -339,6 +339,7 @@ "Cihazın parmak izi sensörlerinde gerçekleştirilen hareketleri yakalayabilir." "Ekran görüntüsü al" "Ekran görüntüsü alınabilir." + "Önizleme, %1$s" "durum çubuğunu devre dışı bırak veya değiştir" "Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir." "durum çubuğunda olma" @@ -1839,6 +1840,8 @@ "Tam ekran olarak görüntüleme" "Çıkmak için yukarıdan aşağıya doğru hızlıca kaydırın." "Anladım" + "Daha iyi bir görünüm elde etmek için ekranı döndürün" + "Daha iyi bir görünüm elde etmek için bölünmüş ekrandan çıkın" "Bitti" "Saat kaydırma çemberi" "Dakika kaydırma çemberi" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 7f5cc3314b8ae1d86e1a48df884ef782bb56f244..c1bb418ad6ccf4083624d059edd910c5298ecf9c 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -341,6 +341,7 @@ "Може фіксувати жести на сканері відбитків пальців." "Робити знімки екрана" "Може робити знімки екрана." + "%1$s (попередній перегляд)" "вимикати чи змін. рядок стану" "Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи." "відображатися як рядок стану" @@ -1841,6 +1842,8 @@ "Перегляд на весь екран" "Щоб вийти, проведіть пальцем зверху вниз." "OK" + "Оберніть для кращого огляду" + "Для кращого огляду вийдіть із режиму розділення екрана" "Готово" "Вибір годин на циферблаті" "Вибір хвилин на циферблаті" diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 4486c55097b443a06b14db231e38c0d91495f57d..2e7c6db9f925b29ddd0ec56b4cdc3bd73530b410 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -339,6 +339,7 @@ "آلہ کے فنگر پرنٹ سینسر پر کیے گئے اشاروں کو کیپچر کر سکتا ہے۔" "اسکرین شاٹ لیں" "ڈسپلے کا اسکرین شاٹ لیا جا سکتا ہے۔" + "پیش منظر، %1$s" "اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں" "ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔" "بطور اسٹیٹس بار کام لیں" @@ -1839,6 +1840,8 @@ "پوری اسکرین میں دیکھ رہے ہیں" "خارج ہونے کیلئے اوپر سے نیچے سوائپ کریں۔" "سمجھ آ گئی" + "بہتر منظر کے لیے گھمائیں" + "بہتر منظر کے لیے اسپلٹ اسکرین سے باہر نکلیں" "ہو گیا" "گھنٹوں کا سرکلر سلائیڈر" "منٹس سرکلر سلائیڈر" diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index a569cdf510ffef1f3c2c762422365aa1b29569af..d1e86f8435633aedb8443004f270851e75431bed 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -339,6 +339,7 @@ "Barmoq izi skanerida kiritilgan ishoralarni taniy oladi." "Skrinshot olish" "Ekrandan skrinshot olishi mumkin." + "Razm solish, %1$s" "holat panelini o‘zgartirish yoki o‘chirish" "Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin." "holat qatorida ko‘rinishi" @@ -1839,6 +1840,8 @@ "Butun ekranli rejim" "Chiqish uchun tepadan pastga torting." "OK" + "Yaxshiroq koʻrish uchun kamerani buring" + "Yaxshiroq koʻrish uchun ajratilgan ekran rejimidan chiqing" "Tayyor" "Doiradan soatni tanlang" "Doiradan daqiqani tanlang" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 2ed1c8b54578ae0a7e99626c7b00971397890f4c..17e1257244c5e2a70db36334212dc7238ebdabf6 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -339,6 +339,7 @@ "Có thể ghi lại các cử chỉ được thực hiện trên cảm biến vân tay của thiết bị." "Chụp ảnh màn hình" "Có thể chụp ảnh màn hình." + "Bản xem trước, %1$s" "vô hiệu hóa hoặc sửa đổi thanh trạng thái" "Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống." "trở thành thanh trạng thái" @@ -1839,6 +1840,8 @@ "Xem toàn màn hình" "Để thoát, hãy vuốt từ trên cùng xuống dưới." "OK" + "Xoay để xem dễ hơn" + "Thoát chế độ chia đôi màn hình để xem dễ hơn" "Xong" "Thanh trượt giờ hình tròn" "Thanh trượt phút hình tròn" diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index c0ecfd8c804318a8e08c1a93e0eab841ca1235f6..aac8d01eeb5d2e18db60cfc74e06091e6e08c36d 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -339,6 +339,7 @@ "可以捕捉在设备指纹传感器上执行的手势。" "截取屏幕截图" "可截取显示画面的屏幕截图。" + "预览,%1$s" "停用或修改状态栏" "允许应用停用状态栏或者增删系统图标。" "用作状态栏" @@ -1839,6 +1840,8 @@ "目前处于全屏模式" "要退出,请从顶部向下滑动。" "知道了" + "旋转可改善预览效果" + "退出分屏可改善预览效果" "完成" "小时转盘" "分钟转盘" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 41db5ad3004b5ead4f7d6f91f9d28234653558a3..4e96e4e58d9d2f5b5910c2e52cd452bea052746c 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -339,6 +339,7 @@ "可以擷取在裝置指紋感應器上執行的手勢。" "擷取螢幕擷圖" "可以擷取螢幕截圖。" + "預覽,%1$s" "停用或修改狀態列" "允許應用程式停用狀態列,並可新增或移除系統圖示。" "成為狀態列" @@ -1839,6 +1840,8 @@ "開啟全螢幕" "由頂部向下滑動即可退出。" "知道了" + "旋轉以改善預覽效果" + "退出分割螢幕,以改善預覽效果" "完成" "小時環形滑桿" "分鐘環形滑桿" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 7ef638f162453fa9294eba88b42d5efc8b9364b4..3f7e50df15c4085efcd4154c03b1c26d1a0d00f9 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -339,6 +339,7 @@ "可以擷取使用者對裝置的指紋感應器執行的手勢。" "擷取螢幕畫面" "可以擷取螢幕畫面。" + "預覽,%1$s" "停用或變更狀態列" "允許應用程式停用狀態列,並可新增或移除系統圖示。" "以狀態列顯示" @@ -1839,6 +1840,8 @@ "以全螢幕檢視" "如要退出,請從畫面頂端向下滑動。" "知道了" + "旋轉螢幕以瀏覽完整的檢視畫面" + "結束分割畫面以全螢幕瀏覽" "完成" "小時數環狀滑桿" "分鐘數環狀滑桿" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 9a21b131c155532d836c04a34dd4958ed3c3d2a5..b0acffc7395158ff018644335d19e4497dfa9215 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -339,6 +339,7 @@ "Ingathatha ukuthinta okwenziwe kunzwa yezigxivizo zeminwe zedivayisi." "Thatha isithombe-skrini" "Ingathatha isithombe-skrini sesiboniso" + "Hlola kuqala, %1$s" "khubaza noma guqula ibha yomumo" "Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo." "yiba yibha yesimo" @@ -1839,6 +1840,8 @@ "Ukubuka isikrini esigcwele" "Ukuze uphume, swayiphela phansi kusuka phezulu." "Ngiyitholile" + "Zungezisa ukuze uthole ukubuka okungcono" + "Phuma ekuhlukaniseni isikrini ukuze ubuke kangcono" "Kwenziwe" "Amahora weslayidi esiyindingilizi" "Amaminithi weslayidi esiyindingilizi" diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index 4b27bf2849fb41bbcca3269986dd29b76a5ec425..fe296c70409572644be20f787ff9017d377c3c5f 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -18,7 +18,6 @@ true false true - false true true false diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index d5875f547e91cbdd3d31e3bc1329ede77aeedff3..b83d3b4ea298f30d831da5c868c84b42e5325969 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -150,6 +150,8 @@ #757575 @color/notification_default_color + 0.38 + 0.12 @color/notification_secondary_text_color_current diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index ea6e1f182fbff9765be7f752d5c07a8acc32f3a6..a99ba152510e994a9664220ef7772470c379ca9e 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -72,8 +72,8 @@ .7 0.60 - 0.10 - 0.10 + 0.5 + 0.5 0.10 diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 031a6669a3c60b798fc2deccf2ef2795508e6ce5..6cb48db10590c444c0fc107ceb8565d75eaa8252 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -560,6 +560,10 @@ rotations as the default behavior. --> false + + false + false + + + + + @@ -652,10 +666,34 @@ --> + + + + + false + + false + + + 0 + + + 0 + + + 0 + @@ -687,20 +725,22 @@ mode. --> 1000 + + 1000 + true - + @@ -729,6 +769,11 @@ we rely on gravity to determine the effective orientation. --> true + + false + true + + 3000 + + + + + false - - true + + false 7 @@ -4947,9 +5000,8 @@ true - - false + + false + + + + + @@ -5271,6 +5328,10 @@ false + + false + false + + false + false @@ -5343,10 +5407,20 @@ split screen. --> false + + false + false + + false + false @@ -6024,4 +6098,17 @@ + + + false + + + -1 + + + false + + + false diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5a1e0e8d6d47983230ac141ca7f139de8db8b5d7..681163dd6c710761a4b825e4c53779b72983f74f 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -968,6 +968,11 @@ Can take a screenshot of the display. + + + + Preview, %1$s + @@ -5153,6 +5158,14 @@ Got it + + Rotate for a better view + + + Exit split screen for a better view + Done @@ -2720,7 +2728,7 @@ - + @@ -2835,7 +2843,6 @@ - @@ -3299,7 +3306,10 @@ + + + @@ -3351,6 +3361,7 @@ + @@ -3432,6 +3443,12 @@ + + + + @@ -4011,9 +4028,15 @@ + + + + + + @@ -4194,6 +4217,7 @@ + @@ -4268,6 +4292,8 @@ + + @@ -4486,6 +4512,7 @@ + @@ -4494,9 +4521,12 @@ + + + @@ -4912,5 +4942,8 @@ + + + diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 3e4b1cc87ef8e0bab0ad2af6d245daeaa19431b4..e96c64206c45191ae81104a861168d38e088667f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1414,6 +1414,7 @@ + diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index ed2b10117308b87d6aacb2ad2a18f1d51784dea9..3768063f2a915cbe725aefc9e70fdb8166328956 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -368,4 +368,20 @@ public class PropertyInvalidatedCacheTests { PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState"); assertEquals(n1, "cache_key.bluetooth.get_state"); } + + @Test + public void testOnTrimMemory() { + TestCache cache = new TestCache(MODULE, "trimMemoryTest"); + // The cache is not active until it has been invalidated once. + cache.invalidateCache(); + // Populate the cache with six entries. + for (int i = 0; i < 6; i++) { + cache.query(i); + } + // The maximum number of entries in TestCache is 4, so even though six entries were + // created, only four are retained. + assertEquals(4, cache.size()); + PropertyInvalidatedCache.onTrimMemory(); + assertEquals(0, cache.size()); + } } diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index eae1bbc930d4abdb0202e916ff0d3e811a91beca..17ed4c478350984becc6429b1346f0abfacba737 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.testng.Assert.assertThrows; @@ -54,4 +56,19 @@ public class ContentCaptureManagerTest { assertThrows(NullPointerException.class, () -> manager.removeData(null)); } + + @Test + @SuppressWarnings("GuardedBy") + public void testFlushViewTreeAppearingEventDisabled_setAndGet() { + final IContentCaptureManager mockService = mock(IContentCaptureManager.class); + final ContentCaptureOptions options = new ContentCaptureOptions(null); + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mockService, options); + + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + manager.setFlushViewTreeAppearingEventDisabled(true); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isTrue(); + manager.setFlushViewTreeAppearingEventDisabled(false); + assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); + } } diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index f370ebd9454551ed94c6e8b1a0fad5af9756bbfe..9d6b29e5c072a923207c8dae1415ffbd1bfbaf24 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -17,6 +17,7 @@ package android.window; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; @@ -60,8 +61,8 @@ public class WindowOnBackInvokedDispatcherTest { private OnBackAnimationCallback mCallback1; @Mock private OnBackAnimationCallback mCallback2; - @Mock - private BackEvent mBackEvent; + private final BackMotionEvent mBackEvent = new BackMotionEvent( + 0, 0, 0, BackEvent.EDGE_LEFT, null); @Before public void setUp() throws Exception { @@ -89,12 +90,12 @@ public class WindowOnBackInvokedDispatcherTest { captor.capture()); captor.getAllValues().get(0).getCallback().onBackStarted(mBackEvent); waitForIdle(); - verify(mCallback1).onBackStarted(mBackEvent); + verify(mCallback1).onBackStarted(any(BackEvent.class)); verifyZeroInteractions(mCallback2); captor.getAllValues().get(1).getCallback().onBackStarted(mBackEvent); waitForIdle(); - verify(mCallback2).onBackStarted(mBackEvent); + verify(mCallback2).onBackStarted(any(BackEvent.class)); verifyNoMoreInteractions(mCallback1); } @@ -114,7 +115,7 @@ public class WindowOnBackInvokedDispatcherTest { assertEquals(captor.getValue().getPriority(), OnBackInvokedDispatcher.PRIORITY_OVERLAY); captor.getValue().getCallback().onBackStarted(mBackEvent); waitForIdle(); - verify(mCallback1).onBackStarted(mBackEvent); + verify(mCallback1).onBackStarted(any(BackEvent.class)); } @Test @@ -152,6 +153,6 @@ public class WindowOnBackInvokedDispatcherTest { verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture()); captor.getValue().getCallback().onBackStarted(mBackEvent); waitForIdle(); - verify(mCallback2).onBackStarted(mBackEvent); + verify(mCallback2).onBackStarted(any(BackEvent.class)); } } diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java new file mode 100644 index 0000000000000000000000000000000000000000..973b904c9344fa856304b0037b9fda119cac2d0c --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.accessibility; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.doubleClick; +import static androidx.test.espresso.action.ViewActions.scrollTo; +import static androidx.test.espresso.action.ViewActions.swipeUp; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withClassName; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.endsWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; + +import androidx.lifecycle.Lifecycle; +import androidx.test.core.app.ActivityScenario; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.Collections; + +/** + * Tests for {@link AccessibilityShortcutChooserActivity}. + */ +@RunWith(AndroidJUnit4.class) +public class AccessibilityShortcutChooserActivityTest { + private static final String ONE_HANDED_MODE = "One-Handed mode"; + private static final String TEST_LABEL = "TEST_LABEL"; + private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class"); + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock + private AccessibilityServiceInfo mAccessibilityServiceInfo; + @Mock + private ResolveInfo mResolveInfo; + @Mock + private ServiceInfo mServiceInfo; + @Mock + private ApplicationInfo mApplicationInfo; + @Mock + private IAccessibilityManager mAccessibilityManagerService; + + @Test + public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() + throws Exception { + configureTestService(); + final ActivityScenario scenario = + ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class); + scenario.moveToState(Lifecycle.State.CREATED); + scenario.moveToState(Lifecycle.State.STARTED); + scenario.moveToState(Lifecycle.State.RESUMED); + + onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot( + isDialog()).check(matches(isDisplayed())); + onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click()); + onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick()); + onView(withId(R.id.accessibility_permission_enable_deny_button)).perform(scrollTo(), + click()); + + onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check( + doesNotExist()); + scenario.moveToState(Lifecycle.State.DESTROYED); + } + + @Test + public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() { + TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true); + final ActivityScenario scenario = + ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class); + scenario.moveToState(Lifecycle.State.CREATED); + scenario.moveToState(Lifecycle.State.STARTED); + scenario.moveToState(Lifecycle.State.RESUMED); + + onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot( + isDialog()).check(matches(isDisplayed())); + onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click()); + onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp()); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(matches(isDisplayed())); + scenario.moveToState(Lifecycle.State.DESTROYED); + } + + @Test + public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() { + TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false); + final ActivityScenario scenario = + ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class); + scenario.moveToState(Lifecycle.State.CREATED); + scenario.moveToState(Lifecycle.State.STARTED); + scenario.moveToState(Lifecycle.State.RESUMED); + + onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot( + isDialog()).check(matches(isDisplayed())); + onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click()); + onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp()); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist()); + scenario.moveToState(Lifecycle.State.DESTROYED); + } + + private void configureTestService() throws Exception { + when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo); + mResolveInfo.serviceInfo = mServiceInfo; + mServiceInfo.applicationInfo = mApplicationInfo; + when(mResolveInfo.loadLabel(any(PackageManager.class))).thenReturn(TEST_LABEL); + when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME); + when(mAccessibilityManagerService.getInstalledAccessibilityServiceList( + anyInt())).thenReturn(Collections.singletonList(mAccessibilityServiceInfo)); + + TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService); + } + + /** + * Used for testing. + */ + public static class TestAccessibilityShortcutChooserActivity extends + AccessibilityShortcutChooserActivity { + private static IAccessibilityManager sAccessibilityManagerService; + + public static void setupForTesting(IAccessibilityManager accessibilityManagerService) { + sAccessibilityManagerService = accessibilityManagerService; + } + + @Override + public Object getSystemService(String name) { + if (Context.ACCESSIBILITY_SERVICE.equals(name) + && sAccessibilityManagerService != null) { + return new AccessibilityManager(this, new Handler(getMainLooper()), + sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true); + } + + return super.getSystemService(name); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 6baf3056be32039dde0b2768070bac6c638fec21..c92ae2c98f9adc4042639cc64a7e36233abb5210 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -21,6 +21,11 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SC import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; +import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -203,6 +208,17 @@ public class AccessibilityShortcutControllerTest { when(mAlertDialog.getWindow()).thenReturn(window); when(mTextToSpeech.getVoice()).thenReturn(mVoice); + + // Clears the sFrameworkShortcutFeaturesMap field which was not properly initialized + // during testing. + try { + Field field = AccessibilityShortcutController.class.getDeclaredField( + "sFrameworkShortcutFeaturesMap"); + field.setAccessible(true); + field.set(window, null); + } catch (Exception e) { + throw new RuntimeException("Unable to set sFrameworkShortcutFeaturesMap", e); + } } @AfterClass @@ -428,11 +444,10 @@ public class AccessibilityShortcutControllerTest { } @Test - public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() { - Map + public void getFrameworkFeatureMap_shouldBeUnmodifiable() { + final Map frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); - assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0); try { frameworkFeatureMap.clear(); @@ -442,6 +457,39 @@ public class AccessibilityShortcutControllerTest { } } + @Test + public void getFrameworkFeatureMap_containsExpectedDefaultKeys() { + final Map + frameworkFeatureMap = + AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); + + assertTrue(frameworkFeatureMap.containsKey(COLOR_INVERSION_COMPONENT_NAME)); + assertTrue(frameworkFeatureMap.containsKey(DALTONIZER_COMPONENT_NAME)); + assertTrue(frameworkFeatureMap.containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME)); + } + + @Test + public void getFrameworkFeatureMap_oneHandedModeEnabled_containsExpectedKey() { + TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true); + + final Map + frameworkFeatureMap = + AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); + + assertTrue(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME)); + } + + @Test + public void getFrameworkFeatureMap_oneHandedModeDisabled_containsExpectedKey() { + TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false); + + final Map + frameworkFeatureMap = + AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); + + assertFalse(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME)); + } + @Test public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash() throws Exception { diff --git a/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ff014add793a3a2f86a7363b8b8e278c68d88198 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.accessibility; + +import com.android.internal.os.RoSystemProperties; + +import java.lang.reflect.Field; + +/** + * Test utility methods. + */ +public class TestUtils { + + /** + * Sets the {@code enabled} of the given OneHandedMode flags to simulate device behavior. + */ + public static void setOneHandedModeEnabled(Object obj, boolean enabled) { + try { + final Field field = RoSystemProperties.class.getDeclaredField( + "SUPPORT_ONE_HANDED_MODE"); + field.setAccessible(true); + field.setBoolean(obj, enabled); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..61899143b9c5e9618a0f860450643d793320bdb1 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app.procstats; + +import static com.android.internal.app.procstats.ProcessStats.STATE_TOP; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.app.ActivityManager; + +import androidx.test.filters.SmallTest; + +import com.android.internal.util.FrameworkStatsLog; + +import junit.framework.TestCase; + +import org.junit.Before; +import org.mockito.Mock; + +import java.util.concurrent.TimeUnit; + +/** Provides test cases for ProcessStats. */ +public class ProcessStatsTest extends TestCase { + + private static final String APP_1_PACKAGE_NAME = "com.android.testapp"; + private static final int APP_1_UID = 5001; + private static final long APP_1_VERSION = 10; + private static final String APP_1_PROCESS_NAME = "com.android.testapp.p"; + private static final String APP_1_SERVICE_NAME = "com.android.testapp.service"; + + private static final String APP_2_PACKAGE_NAME = "com.android.testapp2"; + private static final int APP_2_UID = 5002; + private static final long APP_2_VERSION = 30; + private static final String APP_2_PROCESS_NAME = "com.android.testapp2.p"; + + private static final long NOW_MS = 123000; + private static final int DURATION_SECS = 6; + + @Mock StatsEventOutput mStatsEventOutput; + + @Before + public void setUp() { + initMocks(this); + } + + @SmallTest + public void testDumpProcessState() throws Exception { + ProcessStats processStats = new ProcessStats(); + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processStats.getProcessStateLocked( + APP_2_PACKAGE_NAME, APP_2_UID, APP_2_VERSION, APP_2_PROCESS_NAME); + processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_STATE), + eq(APP_1_UID), + eq(APP_1_PROCESS_NAME), + anyInt(), + anyInt(), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0)); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_STATE), + eq(APP_2_UID), + eq(APP_2_PROCESS_NAME), + anyInt(), + anyInt(), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0)); + } + + @SmallTest + public void testNonZeroProcessStateDuration() throws Exception { + ProcessStats processStats = new ProcessStats(); + ProcessState processState = + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processState.setCombinedState(STATE_TOP, NOW_MS); + processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS)); + processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_STATE), + eq(APP_1_UID), + eq(APP_1_PROCESS_NAME), + anyInt(), + anyInt(), + eq(0), + eq(DURATION_SECS), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0), + eq(0)); + } + + @SmallTest + public void testDumpBoundFgsDuration() throws Exception { + ProcessStats processStats = new ProcessStats(); + ProcessState processState = + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processState.setState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, + ProcessStats.ADJ_MEM_FACTOR_NORMAL, NOW_MS, /* pkgList */ null); + processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS)); + processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_STATE), + eq(APP_1_UID), + eq(APP_1_PROCESS_NAME), + anyInt(), + anyInt(), + eq(0), + eq(0), + eq(0), + eq(0), + eq(DURATION_SECS), + eq(0), + eq(0), + eq(0), + eq(0)); + } + + @SmallTest + public void testDumpProcessAssociation() throws Exception { + ProcessStats processStats = new ProcessStats(); + AssociationState associationState = + processStats.getAssociationStateLocked( + APP_1_PACKAGE_NAME, + APP_1_UID, + APP_1_VERSION, + APP_1_PROCESS_NAME, + APP_1_SERVICE_NAME); + AssociationState.SourceState sourceState = + associationState.startSource(APP_2_UID, APP_2_PROCESS_NAME, APP_2_PACKAGE_NAME); + sourceState.stop(); + processStats.dumpProcessAssociation( + FrameworkStatsLog.PROCESS_ASSOCIATION, mStatsEventOutput); + verify(mStatsEventOutput) + .write( + eq(FrameworkStatsLog.PROCESS_ASSOCIATION), + eq(APP_2_UID), + eq(APP_2_PROCESS_NAME), + eq(APP_1_UID), + eq(APP_1_SERVICE_NAME), + anyInt(), + anyInt(), + eq(0), + eq(0), + eq(0), + eq(APP_1_PROCESS_NAME)); + } + + @SmallTest + public void testSafelyResetClearsProcessInUidState() throws Exception { + ProcessStats processStats = new ProcessStats(); + ProcessState processState = + processStats.getProcessStateLocked( + APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME); + processState.makeActive(); + UidState uidState = processStats.mUidStates.get(APP_1_UID); + assertTrue(uidState.isInUse()); + processState.makeInactive(); + uidState.resetSafely(NOW_MS); + processState.makeActive(); + assertFalse(uidState.isInUse()); + } +} diff --git a/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS b/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..2e96c97c8bb352f425747ce548ea1a34f89f0dc0 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/config/sysui/OWNERS @@ -0,0 +1 @@ +include /packages/SystemUI/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java b/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6b9d39ceb79a8afabbbb237c10c5d1d2c0f11dca --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/config/sysui/SystemUiSystemPropertiesFlagsTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.config.sysui; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; + +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver; + +import junit.framework.TestCase; + +import java.util.HashMap; +import java.util.Map; + +@SmallTest +public class SystemUiSystemPropertiesFlagsTest extends TestCase { + + public class TestableDebugResolver extends SystemUiSystemPropertiesFlags.DebugResolver { + final Map mTestData = new HashMap<>(); + + @Override + public boolean getBoolean(String key, boolean defaultValue) { + Boolean testValue = mTestData.get(key); + return testValue == null ? defaultValue : testValue; + } + + public void set(Flag flag, Boolean value) { + mTestData.put(flag.mSysPropKey, value); + } + } + + private FlagResolver mProdResolver; + private TestableDebugResolver mDebugResolver; + + private Flag mReleasedFlag; + private Flag mTeamfoodFlag; + private Flag mDevFlag; + + public void setUp() { + mProdResolver = new SystemUiSystemPropertiesFlags.ProdResolver(); + mDebugResolver = new TestableDebugResolver(); + mReleasedFlag = SystemUiSystemPropertiesFlags.releasedFlag("mReleasedFlag"); + mTeamfoodFlag = SystemUiSystemPropertiesFlags.teamfoodFlag("mTeamfoodFlag"); + mDevFlag = SystemUiSystemPropertiesFlags.devFlag("mDevFlag"); + } + + public void tearDown() { + SystemUiSystemPropertiesFlags.TEST_RESOLVER = null; + } + + public void testProdResolverReturnsDefault() { + assertThat(mProdResolver.isEnabled(mReleasedFlag)).isTrue(); + assertThat(mProdResolver.isEnabled(mTeamfoodFlag)).isFalse(); + assertThat(mProdResolver.isEnabled(mDevFlag)).isFalse(); + } + + public void testDebugResolverAndReleasedFlag() { + assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isTrue(); + + mDebugResolver.set(mReleasedFlag, false); + assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isFalse(); + + mDebugResolver.set(mReleasedFlag, true); + assertThat(mDebugResolver.isEnabled(mReleasedFlag)).isTrue(); + } + + private void assertTeamfoodFlag(Boolean flagValue, Boolean teamfood, boolean expected) { + mDebugResolver.set(mTeamfoodFlag, flagValue); + mDebugResolver.set(SystemUiSystemPropertiesFlags.TEAMFOOD, teamfood); + assertThat(mDebugResolver.isEnabled(mTeamfoodFlag)).isEqualTo(expected); + } + + public void testDebugResolverAndTeamfoodFlag() { + assertTeamfoodFlag(null, null, false); + assertTeamfoodFlag(true, null, true); + assertTeamfoodFlag(false, null, false); + assertTeamfoodFlag(null, true, true); + assertTeamfoodFlag(true, true, true); + assertTeamfoodFlag(false, true, false); + assertTeamfoodFlag(null, false, false); + assertTeamfoodFlag(true, false, true); + assertTeamfoodFlag(false, false, false); + } + + public void testDebugResolverAndDevFlag() { + assertThat(mDebugResolver.isEnabled(mDevFlag)).isFalse(); + + mDebugResolver.set(mDevFlag, true); + assertThat(mDebugResolver.isEnabled(mDevFlag)).isTrue(); + + mDebugResolver.set(mDevFlag, false); + assertThat(mDebugResolver.isEnabled(mDevFlag)).isFalse(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 52feac5a585a7e9c61a5a78d95ceec91853eeda1..4c9b2b7f5dd67a14679326cd64fee360737e65aa 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -360,6 +360,7 @@ public class BatteryStatsNoteTest extends TestCase { // map of ActivityManager process states and how long to simulate run time in each state Map stateRuntimeMap = new HashMap(); stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP, 1111); + stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_TOP, 7382); stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 1234); stateRuntimeMap.put(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 2468); stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP_SLEEPING, 7531); @@ -396,7 +397,8 @@ public class BatteryStatsNoteTest extends TestCase { actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, elapsedTimeUs, STATS_SINCE_CHARGED); - expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) + + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING, @@ -406,8 +408,7 @@ public class BatteryStatsNoteTest extends TestCase { actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND, elapsedTimeUs, STATS_SINCE_CHARGED); - expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) - + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); + expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, @@ -415,7 +416,8 @@ public class BatteryStatsNoteTest extends TestCase { expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BACKUP) + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_SERVICE) - + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER); + + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER) + + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_TOP); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_CACHED, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java index 354b93709976282337aa20e70ecb29d25262c0be..2742861351747ee37eb166b9da2ab672485fc751 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -78,9 +78,9 @@ public class BatteryUsageStatsProviderTest { batteryUsageStats.getUidBatteryConsumers(); final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND)) - .isEqualTo(60 * MINUTE_IN_MS); + .isEqualTo(20 * MINUTE_IN_MS); assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) - .isEqualTo(10 * MINUTE_IN_MS); + .isEqualTo(40 * MINUTE_IN_MS); assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO)) .isWithin(PRECISION).of(2.0); assertThat( @@ -121,22 +121,44 @@ public class BatteryUsageStatsProviderTest { private BatteryStatsImpl prepareBatteryStats() { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); - batteryStats.noteActivityResumedLocked(APP_UID, - 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); - batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP, - 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); - batteryStats.noteActivityPausedLocked(APP_UID, - 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); - batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE, - 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); - batteryStats.noteUidProcessStateLocked(APP_UID, - ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, - 40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); - batteryStats.noteUidProcessStateLocked(APP_UID, - ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, - 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); - batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY, - 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS); + mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteActivityResumedLocked(APP_UID); + } + + mStatsRule.setTime(10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP); + } + mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteActivityPausedLocked(APP_UID); + } + mStatsRule.setTime(30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, + ActivityManager.PROCESS_STATE_SERVICE); + } + mStatsRule.setTime(40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + mStatsRule.setTime(50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, + ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); + } + mStatsRule.setTime(60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, + ActivityManager.PROCESS_STATE_BOUND_TOP); + } + mStatsRule.setTime(70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS); + synchronized (batteryStats) { + batteryStats.noteUidProcessStateLocked(APP_UID, + ActivityManager.PROCESS_STATE_CACHED_EMPTY); + } batteryStats.noteFlashlightOnLocked(APP_UID, 1000, 1000); batteryStats.noteFlashlightOffLocked(APP_UID, 5000, 5000); diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index 00ac1985f897d12eb2e7175a66d3853bc860da52..0bdf491e63776a0e048f08817977a6a1529fd78d 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -245,6 +245,8 @@ public class MobileRadioPowerCalculatorTest { stats.noteNetworkInterfaceForTransports("cellular", new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + stats.notePhoneOnLocked(9800, 9800); + // Note application network activity NetworkStats networkStats = new NetworkStats(10000, 1) .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, @@ -257,27 +259,33 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setTime(12_000, 12_000); - MobileRadioPowerCalculator calculator = + MobileRadioPowerCalculator mobileRadioPowerCalculator = new MobileRadioPowerCalculator(mStatsRule.getPowerProfile()); - - mStatsRule.apply(calculator); + PhonePowerCalculator phonePowerCalculator = + new PhonePowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(mobileRadioPowerCalculator, phonePowerCalculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(1.53934); + .isWithin(PRECISION).of(1.38541); assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(2.77778); + .isWithin(PRECISION).of(2.5); assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) + .isWithin(PRECISION).of(0.27778); + assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) + .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isWithin(PRECISION).of(1.53934); + .isWithin(PRECISION).of(1.38541); assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); } diff --git a/core/tests/screenshothelpertests/Android.bp b/core/tests/screenshothelpertests/Android.bp index 37af99c58d427b77877aaba0917616c720c44bf3..3c71e6e4247b733236a8d1eacdb0887a1cace15d 100644 --- a/core/tests/screenshothelpertests/Android.bp +++ b/core/tests/screenshothelpertests/Android.bp @@ -13,7 +13,7 @@ android_test { srcs: [ "src/**/*.java", ], - + static_libs: [ "frameworks-base-testutils", "androidx.test.runner", @@ -21,6 +21,7 @@ android_test { "androidx.test.ext.junit", "mockito-target-minus-junit4", "platform-test-annotations", + "testng", ], libs: [ diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java index 2719431a536e414f5cbaef162005455f6a03aedf..5c9894ebd5900329349dee999288411df7e7c4d5 100644 --- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java @@ -17,6 +17,7 @@ package com.android.internal.util; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; @@ -31,9 +32,11 @@ import static org.mockito.Mockito.mock; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.Insets; import android.graphics.Rect; -import android.os.Bundle; +import android.hardware.HardwareBuffer; import android.os.Handler; import android.os.Looper; import android.view.WindowManager; @@ -79,30 +82,48 @@ public final class ScreenshotHelperTest { @Test public void testFullscreenScreenshot() { - mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, + mScreenshotHelper.takeScreenshot( WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null); } + @Test + public void testFullscreenScreenshotRequest() { + ScreenshotRequest request = new ScreenshotRequest.Builder( + TAKE_SCREENSHOT_FULLSCREEN, WindowManager.ScreenshotSource.SCREENSHOT_OTHER) + .build(); + mScreenshotHelper.takeScreenshot(request, mHandler, null); + } + @Test public void testProvidedImageScreenshot() { - mScreenshotHelper.provideScreenshot( - new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""), - WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null); + HardwareBuffer buffer = HardwareBuffer.create( + 10, 10, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); + Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB)); + ScreenshotRequest request = new ScreenshotRequest.Builder( + TAKE_SCREENSHOT_PROVIDED_IMAGE, WindowManager.ScreenshotSource.SCREENSHOT_OTHER) + .setTopComponent(new ComponentName("", "")) + .setTaskId(1) + .setUserId(1) + .setBitmap(bitmap) + .setBoundsOnScreen(new Rect()) + .setInsets(Insets.NONE) + .build(); + mScreenshotHelper.takeScreenshot(request, mHandler, null); } @Test public void testScreenshotTimesOut() { long timeoutMs = 10; + ScreenshotRequest request = new ScreenshotRequest.Builder( + TAKE_SCREENSHOT_FULLSCREEN, WindowManager.ScreenshotSource.SCREENSHOT_OTHER) + .build(); CountDownLatch lock = new CountDownLatch(1); - mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, - WindowManager.ScreenshotSource.SCREENSHOT_OTHER, - mHandler, - timeoutMs, + mScreenshotHelper.takeScreenshotInternal(request, mHandler, uri -> { assertNull(uri); lock.countDown(); - }); + }, timeoutMs); try { // Add tolerance for delay to prevent flakes. diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..89acbc7986fb9f599d494ea07412112163e5674b --- /dev/null +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.os.UserHandle.USER_NULL; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER; +import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; +import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import static org.testng.Assert.assertThrows; + +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.os.Parcel; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public final class ScreenshotRequestTest { + private final ComponentName mComponentName = + new ComponentName("android.test", "android.test.Component"); + + @Test + public void testSimpleScreenshot() { + ScreenshotRequest in = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build(); + + Parcel parcel = Parcel.obtain(); + in.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel); + + assertEquals(TAKE_SCREENSHOT_FULLSCREEN, out.getType()); + assertEquals(SCREENSHOT_OTHER, out.getSource()); + assertNull("Top component was expected to be null", out.getTopComponent()); + assertEquals(INVALID_TASK_ID, out.getTaskId()); + assertEquals(USER_NULL, out.getUserId()); + assertNull("Bitmap was expected to be null", out.getBitmap()); + assertNull("Bounds were expected to be null", out.getBoundsInScreen()); + assertEquals(Insets.NONE, out.getInsets()); + } + + @Test + public void testProvidedScreenshot() { + Bitmap bitmap = makeHardwareBitmap(50, 50); + ScreenshotRequest in = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER) + .setTopComponent(mComponentName) + .setTaskId(2) + .setUserId(3) + .setBitmap(bitmap) + .setBoundsOnScreen(new Rect(10, 10, 60, 60)) + .setInsets(Insets.of(2, 3, 4, 5)) + .build(); + + Parcel parcel = Parcel.obtain(); + in.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel); + + assertEquals(TAKE_SCREENSHOT_PROVIDED_IMAGE, out.getType()); + assertEquals(SCREENSHOT_OTHER, out.getSource()); + assertEquals(mComponentName, out.getTopComponent()); + assertEquals(2, out.getTaskId()); + assertEquals(3, out.getUserId()); + assertTrue("Bitmaps should be equal", out.getBitmap().sameAs(bitmap)); + assertEquals(new Rect(10, 10, 60, 60), out.getBoundsInScreen()); + assertEquals(Insets.of(2, 3, 4, 5), out.getInsets()); + } + + @Test + public void testProvidedScreenshot_nullBitmap() { + ScreenshotRequest.Builder inBuilder = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER) + .setTopComponent(mComponentName) + .setTaskId(2) + .setUserId(3) + .setBoundsOnScreen(new Rect(10, 10, 60, 60)) + .setInsets(Insets.of(2, 3, 4, 5)); + + assertThrows(IllegalStateException.class, inBuilder::build); + } + + @Test + public void testFullScreenshot_withBitmap() { + // A bitmap added to a FULLSCREEN request will be ignored, but it's technically valid + Bitmap bitmap = makeHardwareBitmap(50, 50); + ScreenshotRequest in = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER) + .setBitmap(bitmap) + .build(); + + Parcel parcel = Parcel.obtain(); + in.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + ScreenshotRequest out = ScreenshotRequest.CREATOR.createFromParcel(parcel); + + assertEquals(TAKE_SCREENSHOT_FULLSCREEN, out.getType()); + assertEquals(SCREENSHOT_OTHER, out.getSource()); + assertNull(out.getTopComponent()); + assertEquals(INVALID_TASK_ID, out.getTaskId()); + assertEquals(USER_NULL, out.getUserId()); + assertTrue("Bitmaps should be equal", out.getBitmap().sameAs(bitmap)); + assertNull("Bounds expected to be null", out.getBoundsInScreen()); + assertEquals(Insets.NONE, out.getInsets()); + } + + @Test + public void testInvalidType() { + assertThrows(IllegalArgumentException.class, + () -> new ScreenshotRequest.Builder(5, 2).build()); + } + + private Bitmap makeHardwareBitmap(int width, int height) { + HardwareBuffer buffer = HardwareBuffer.create( + width, height, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); + return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB)); + } +} diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index e0e13f59b706588c9611182ec92c7f520454a77d..6dcee6d8bd311cf48a2ae137998fe2a82a96d1af 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -49,6 +49,7 @@ + diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index ff42fb52d8e7346005b3fa2cec7742971372a7ab..5703b6f687eed3c1a13e3c08cf76ed21647f1a07 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -306,6 +306,7 @@ applications that come with the platform + @@ -516,6 +517,12 @@ applications that come with the platform + + + + + + diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index f47d9c6e0c2d65d843d589daa970a40c586e5a1e..334a7275ebe2822b2b8858f434fa6e923a083fb7 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -469,6 +469,18 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RecentTasks.java" }, + "-1643780158": { + "message": "Saving original orientation before camera compat, last orientation is %d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" + }, + "-1639406696": { + "message": "NOSENSOR override detected", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" + }, "-1638958146": { "message": "Removing activity %s from task=%s adding to task=%s Callers=%s", "level": "INFO", @@ -751,6 +763,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, + "-1397175017": { + "message": "Other orientation overrides are in place: not reverting", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" + }, "-1394745488": { "message": "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s", "level": "INFO", @@ -1051,6 +1069,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowContainer.java" }, + "-1104347731": { + "message": "Setting requested orientation %s for %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1103716954": { "message": "Not removing %s due to exit animation", "level": "VERBOSE", @@ -1663,6 +1687,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-529187878": { + "message": "Reverting orientation after camera compat force rotation", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" + }, "-521613870": { "message": "App died during pause, not stopping: %s", "level": "VERBOSE", @@ -2359,6 +2389,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "138097009": { + "message": "NOSENSOR override is absent: reverting", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationReversionController.java" + }, "140319294": { "message": "IME target changed within ActivityRecord", "level": "DEBUG", @@ -3331,6 +3367,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/TaskFragment.java" }, + "1015746067": { + "message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "1022095595": { "message": "TaskFragment info changed name=%s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 14dc6a2513a0b73b929c60050e3b405570c1ecd6..6b1cf8b1ed2af1e221305eabf9ac391b5fbfbd1c 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -23,7 +23,6 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.ValueAnimator; -import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -983,9 +982,9 @@ public class RippleDrawable extends LayerDrawable { RippleShader shader = new RippleShader(); // Grab the color for the current state and cut the alpha channel in // half so that the ripple and background together yield full alpha. - final int color = clampAlpha(mMaskColorFilter == null + final int color = mMaskColorFilter == null ? mState.mColor.getColorForState(getState(), Color.BLACK) - : mMaskColorFilter.getColor()); + : mMaskColorFilter.getColor(); final int effectColor = mState.mEffectColor.getColorForState(getState(), Color.MAGENTA); final float noisePhase = AnimationUtils.currentAnimationTimeMillis(); shader.setColor(color, effectColor); @@ -1008,13 +1007,6 @@ public class RippleDrawable extends LayerDrawable { return properties; } - private int clampAlpha(@ColorInt int color) { - if (Color.alpha(color) < 128) { - return (color & 0x00FFFFFF) | 0x80000000; - } - return color; - } - @Override public void invalidateSelf() { invalidateSelf(true); @@ -1229,7 +1221,7 @@ public class RippleDrawable extends LayerDrawable { // Grab the color for the current state and cut the alpha channel in // half so that the ripple and background together yield full alpha. - final int color = clampAlpha(mState.mColor.getColorForState(getState(), Color.BLACK)); + final int color = mState.mColor.getColorForState(getState(), Color.BLACK); final Paint p = mRipplePaint; if (mMaskColorFilter != null) { diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index dc4b5636a246e1dc936e96b271ea2914b4257752..a5b192cd7ceb730f58a19760ad874065aab29f53 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -63,6 +63,12 @@ android_library_import { sdk_version: "current", } +android_library_import { + name: "window-extensions-core", + aars: ["window-extensions-core-release.aar"], + sdk_version: "current", +} + java_library { name: "androidx.window.extensions", srcs: [ @@ -70,7 +76,10 @@ java_library { "src/androidx/window/util/**/*.java", "src/androidx/window/common/**/*.java", ], - static_libs: ["window-extensions"], + static_libs: [ + "window-extensions", + "window-extensions-core", + ], installable: true, sdk_version: "core_platform", system_ext_specific: true, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 54edd9ec4335871d110c4a87421f9ae5996baae6..666b472c3716fca645382d544306e3ef16714f99 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 1; + return 2; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index 3adae7006369d15a10b012311d3818a16be21d12..9118ee2bf125c6080bc9525a1d1a7d7b59c1ed9a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -25,12 +25,12 @@ import android.hardware.devicestate.DeviceStateRequest; import android.util.ArraySet; import androidx.annotation.NonNull; +import androidx.window.extensions.core.util.function.Consumer; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * Reference implementation of androidx.window.extensions.area OEM interface for use with @@ -187,6 +187,10 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private int getCurrentStatus() { + if (mRearDisplayState == INVALID_DEVICE_STATE) { + return WindowAreaComponent.STATUS_UNSUPPORTED; + } + if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE || isRearDisplayActive()) { return WindowAreaComponent.STATUS_UNAVAILABLE; @@ -252,4 +256,37 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, } } } + + @Override + public void addRearDisplayPresentationStatusListener( + @NonNull Consumer consumer) { + throw new UnsupportedOperationException( + "addRearDisplayPresentationStatusListener is not supported in API_VERSION=2"); + } + + @Override + public void removeRearDisplayPresentationStatusListener( + @NonNull Consumer consumer) { + throw new UnsupportedOperationException( + "removeRearDisplayPresentationStatusListener is not supported in API_VERSION=2"); + } + + @Override + public void startRearDisplayPresentationSession(@NonNull Activity activity, + @NonNull Consumer<@WindowAreaSessionState Integer> consumer) { + throw new UnsupportedOperationException( + "startRearDisplayPresentationSession is not supported in API_VERSION=2"); + } + + @Override + public void endRearDisplayPresentationSession() { + throw new UnsupportedOperationException( + "endRearDisplayPresentationSession is not supported in API_VERSION=2"); + } + + @Override + public ExtensionWindowAreaPresentation getRearDisplayPresentation() { + throw new UnsupportedOperationException( + "getRearDisplayPresentation is not supported in API_VERSION=2"); + } } 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 87fa63d7fe147039de53794d5fc423daa974bd67..00e13c94ea905557d0a1f364b986cdf30e929a43 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -191,10 +191,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { */ void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) { + createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode, + null /* pairedActivityToken */); + } + + /** + * @param ownerToken The token of the activity that creates this task fragment. It does not + * have to be a child of this task fragment, but must belong to the same task. + * @param pairedActivityToken The token of the activity that will be reparented to this task + * fragment. When it is not {@code null}, the task fragment will be + * positioned right above it. + */ + void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, + @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode, + @Nullable IBinder pairedActivityToken) { final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( getOrganizerToken(), fragmentToken, ownerToken) .setInitialBounds(bounds) .setWindowingMode(windowingMode) + .setPairedActivityToken(pairedActivityToken) .build(); createTaskFragment(wct, fragmentOptions); } @@ -216,8 +231,10 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { private void createTaskFragmentAndReparentActivity(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode, @NonNull Activity activity) { - createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode); - wct.reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken()); + final IBinder reparentActivityToken = activity.getActivityToken(); + createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode, + reparentActivityToken); + wct.reparentActivityToTaskFragment(fragmentToken, reparentActivityToken); } void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, 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 1cd3ea5592e3bd3a554c47b70c4f6e696ba3c9af..569eb801989bc05136b24a25069ca591e379a750 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -77,6 +77,9 @@ import androidx.annotation.Nullable; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; +import androidx.window.extensions.WindowExtensionsImpl; +import androidx.window.extensions.core.util.function.Consumer; +import androidx.window.extensions.core.util.function.Function; import androidx.window.extensions.embedding.TransactionManager.TransactionRecord; import androidx.window.extensions.layout.WindowLayoutComponentImpl; @@ -87,7 +90,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * Main controller class that manages split states and presentation. @@ -113,7 +115,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** * A developer-defined {@link SplitAttributes} calculator to compute the current * {@link SplitAttributes} with the current device and window states. - * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)} + * It is registered via {@link #setSplitAttributesCalculator(Function)} * and unregistered via {@link #clearSplitAttributesCalculator()}. * This is called when: *

      @@ -126,7 +128,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ @GuardedBy("mLock") @Nullable - private SplitAttributesCalculator mSplitAttributesCalculator; + private Function mSplitAttributesCalculator; /** * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info @@ -139,6 +141,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final SparseArray mTaskContainers = new SparseArray<>(); /** Callback to Jetpack to notify about changes to split states. */ + @GuardedBy("mLock") @Nullable private Consumer> mEmbeddingCallback; private final List mLastReportedSplitStates = new ArrayList<>(); @@ -164,7 +167,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener()); } - private class FoldingFeatureListener implements Consumer> { + private class FoldingFeatureListener + implements java.util.function.Consumer> { @Override public void accept(List foldingFeatures) { synchronized (mLock) { @@ -205,7 +209,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override - public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) { + public void setSplitAttributesCalculator( + @NonNull Function calculator) { synchronized (mLock) { mSplitAttributesCalculator = calculator; } @@ -220,7 +225,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") @Nullable - SplitAttributesCalculator getSplitAttributesCalculator() { + Function getSplitAttributesCalculator() { return mSplitAttributesCalculator; } @@ -233,9 +238,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** * Registers the split organizer callback to notify about changes to active splits. + * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with + * {@link WindowExtensionsImpl#getVendorApiLevel()} 2. */ + @Deprecated @Override - public void setSplitInfoCallback(@NonNull Consumer> callback) { + public void setSplitInfoCallback( + @NonNull java.util.function.Consumer> callback) { + Consumer> oemConsumer = callback::accept; + setSplitInfoCallback(oemConsumer); + } + + /** + * Registers the split organizer callback to notify about changes to active splits. + * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2 + */ + public void setSplitInfoCallback(Consumer> callback) { synchronized (mLock) { mEmbeddingCallback = callback; updateCallbackIfNecessary(); @@ -1481,7 +1499,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns the active split that has the provided containers as primary and secondary or as * secondary and primary, if available. */ - @VisibleForTesting + @GuardedBy("mLock") @Nullable SplitContainer getActiveSplitForContainers( @NonNull TaskFragmentContainer firstContainer, @@ -2138,4 +2156,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return configuration != null && configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; } + + @Override + public ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options, + @NonNull IBinder token) { + throw new UnsupportedOperationException( + "setLaunchingActivityStack is not supported in API_VERSION=2"); + } + + @Override + public void finishActivityStacks(@NonNull Set activityStackTokens) { + throw new UnsupportedOperationException( + "finishActivityStacks is not supported in API_VERSION=2"); + } + + @Override + public void invalidateTopVisibleSplitAttributes() { + throw new UnsupportedOperationException( + "invalidateTopVisibleSplitAttributes is not supported in API_VERSION=2"); + } + + @Override + public void updateSplitAttributes(@NonNull IBinder splitInfoToken, + @NonNull SplitAttributes splitAttributes) { + throw new UnsupportedOperationException( + "updateSplitAttributes is not supported in API_VERSION=2"); + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 14d244bbb6cee1300e2659ff4685ce435d3c7193..c23ac75e696f709a2bedf8f1923ccea95e214649 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -43,11 +43,11 @@ import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.window.extensions.core.util.function.Function; import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType; -import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams; import androidx.window.extensions.embedding.TaskContainer.TaskProperties; import androidx.window.extensions.layout.DisplayFeature; import androidx.window.extensions.layout.FoldingFeature; @@ -268,10 +268,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container = mController.newContainer(activity, taskId); final int windowingMode = mController.getTaskContainer(taskId) .getWindowingModeForSplitTaskFragment(bounds); - createTaskFragment(wct, container.getTaskFragmentToken(), activity.getActivityToken(), - bounds, windowingMode); + final IBinder reparentActivityToken = activity.getActivityToken(); + createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken, + bounds, windowingMode, reparentActivityToken); wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(), - activity.getActivityToken()); + reparentActivityToken); } else { resizeTaskFragmentIfRegistered(wct, container, bounds); final int windowingMode = mController.getTaskContainer(taskId) @@ -551,11 +552,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull SplitRule rule, @Nullable Pair minDimensionsPair) { final Configuration taskConfiguration = taskProperties.getConfiguration(); final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration); - final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator(); + final Function calculator = + mController.getSplitAttributesCalculator(); final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes(); - final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics); + final boolean areDefaultConstraintsSatisfied = rule.checkParentMetrics(taskWindowMetrics); if (calculator == null) { - if (!isDefaultMinSizeSatisfied) { + if (!areDefaultConstraintsSatisfied) { return EXPAND_CONTAINERS_ATTRIBUTES; } return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes, @@ -565,9 +567,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(), taskConfiguration.windowConfiguration); final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams( - taskWindowMetrics, taskConfiguration, defaultSplitAttributes, - isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag()); - final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params); + taskWindowMetrics, taskConfiguration, windowLayoutInfo, defaultSplitAttributes, + areDefaultConstraintsSatisfied, rule.getTag()); + final SplitAttributes splitAttributes = calculator.apply(params); return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair); } @@ -659,21 +661,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull SplitAttributes splitAttributes) { final Configuration taskConfiguration = taskProperties.getConfiguration(); final FoldingFeature foldingFeature = getFoldingFeature(taskProperties); - final SplitType splitType = computeSplitType(splitAttributes, taskConfiguration, - foldingFeature); - final SplitAttributes computedSplitAttributes = new SplitAttributes.Builder() - .setSplitType(splitType) - .setLayoutDirection(splitAttributes.getLayoutDirection()) - .build(); - if (!shouldShowSplit(computedSplitAttributes)) { + if (!shouldShowSplit(splitAttributes)) { return new Rect(); } switch (position) { case POSITION_START: - return getPrimaryBounds(taskConfiguration, computedSplitAttributes, foldingFeature); + return getPrimaryBounds(taskConfiguration, splitAttributes, foldingFeature); case POSITION_END: - return getSecondaryBounds(taskConfiguration, computedSplitAttributes, - foldingFeature); + return getSecondaryBounds(taskConfiguration, splitAttributes, foldingFeature); case POSITION_FILL: default: return new Rect(); @@ -683,63 +678,76 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull private Rect getPrimaryBounds(@NonNull Configuration taskConfiguration, @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) { - if (!shouldShowSplit(splitAttributes)) { + final SplitAttributes computedSplitAttributes = updateSplitAttributesType(splitAttributes, + computeSplitType(splitAttributes, taskConfiguration, foldingFeature)); + if (!shouldShowSplit(computedSplitAttributes)) { return new Rect(); } - switch (splitAttributes.getLayoutDirection()) { + switch (computedSplitAttributes.getLayoutDirection()) { case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT: { - return getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getLeftContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT: { - return getRightContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getRightContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.LOCALE: { final boolean isLtr = taskConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; return isLtr - ? getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature) - : getRightContainerBounds(taskConfiguration, splitAttributes, + ? getLeftContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature) + : getRightContainerBounds(taskConfiguration, computedSplitAttributes, foldingFeature); } case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM: { - return getTopContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getTopContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP: { - return getBottomContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getBottomContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } default: throw new IllegalArgumentException("Unknown layout direction:" - + splitAttributes.getLayoutDirection()); + + computedSplitAttributes.getLayoutDirection()); } } @NonNull private Rect getSecondaryBounds(@NonNull Configuration taskConfiguration, @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) { - if (!shouldShowSplit(splitAttributes)) { + final SplitAttributes computedSplitAttributes = updateSplitAttributesType(splitAttributes, + computeSplitType(splitAttributes, taskConfiguration, foldingFeature)); + if (!shouldShowSplit(computedSplitAttributes)) { return new Rect(); } - switch (splitAttributes.getLayoutDirection()) { + switch (computedSplitAttributes.getLayoutDirection()) { case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT: { - return getRightContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getRightContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT: { - return getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getLeftContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.LOCALE: { final boolean isLtr = taskConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; return isLtr - ? getRightContainerBounds(taskConfiguration, splitAttributes, + ? getRightContainerBounds(taskConfiguration, computedSplitAttributes, foldingFeature) - : getLeftContainerBounds(taskConfiguration, splitAttributes, + : getLeftContainerBounds(taskConfiguration, computedSplitAttributes, foldingFeature); } case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM: { - return getBottomContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getBottomContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP: { - return getTopContainerBounds(taskConfiguration, splitAttributes, foldingFeature); + return getTopContainerBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); } default: throw new IllegalArgumentException("Unknown layout direction:" @@ -747,6 +755,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } } + /** + * Returns the {@link SplitAttributes} that update the {@link SplitType} to + * {@code splitTypeToUpdate}. + */ + private static SplitAttributes updateSplitAttributesType( + @NonNull SplitAttributes splitAttributes, @NonNull SplitType splitTypeToUpdate) { + return new SplitAttributes.Builder() + .setSplitType(splitTypeToUpdate) + .setLayoutDirection(splitAttributes.getLayoutDirection()) + .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor()) + .build(); + } + @NonNull private Rect getLeftContainerBounds(@NonNull Configuration taskConfiguration, @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) { @@ -839,7 +860,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } @Nullable - private FoldingFeature getFoldingFeature(@NonNull TaskProperties taskProperties) { + @VisibleForTesting + FoldingFeature getFoldingFeature(@NonNull TaskProperties taskProperties) { final int displayId = taskProperties.getDisplayId(); final WindowConfiguration windowConfiguration = taskProperties.getConfiguration() .windowConfiguration; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java index dcc12ac075891f2043f4468b7fe9ca3c1be939dc..b917ac80256c506c628f5cd08fbf3e623ffedc76 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java @@ -215,6 +215,8 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub { } else { closingTargets.add(target); closingWholeScreenBounds.union(target.screenSpaceBounds); + // Union the start bounds since this may be the ClosingChanging animation. + closingWholeScreenBounds.union(target.startBounds); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 076856c373d62aac01066ec81f7fcd121413d0ff..17814c65e791ec5402935a9e3658479d589d6e31 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -141,12 +141,26 @@ class TaskFragmentContainer { mToken = new Binder("TaskFragmentContainer"); mTaskContainer = taskContainer; if (pairedPrimaryContainer != null) { + // The TaskFragment will be positioned right above the paired container. if (pairedPrimaryContainer.getTaskContainer() != taskContainer) { throw new IllegalArgumentException( "pairedPrimaryContainer must be in the same Task"); } final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer); taskContainer.mContainers.add(primaryIndex + 1, this); + } else if (pendingAppearedActivity != null) { + // The TaskFragment will be positioned right above the pending appeared Activity. If any + // existing TaskFragment is empty with pending Intent, it is likely that the Activity of + // the pending Intent hasn't been created yet, so the new Activity should be below the + // empty TaskFragment. + int i = taskContainer.mContainers.size() - 1; + for (; i >= 0; i--) { + final TaskFragmentContainer container = taskContainer.mContainers.get(i); + if (!container.isEmpty() || container.getPendingAppearedIntent() == null) { + break; + } + } + taskContainer.mContainers.add(i + 1, this); } else { taskContainer.mContainers.add(this); } @@ -500,6 +514,8 @@ class TaskFragmentContainer { } if (!shouldFinishDependent) { + // Always finish the placeholder when the primary is finished. + finishPlaceholderIfAny(wct, presenter); return; } @@ -526,6 +542,28 @@ class TaskFragmentContainer { mActivitiesToFinishOnExit.clear(); } + @GuardedBy("mController.mLock") + private void finishPlaceholderIfAny(@NonNull WindowContainerTransaction wct, + @NonNull SplitPresenter presenter) { + final List containersToRemove = new ArrayList<>(); + for (TaskFragmentContainer container : mContainersToFinishOnExit) { + if (container.mIsFinished) { + continue; + } + final SplitContainer splitContainer = mController.getActiveSplitForContainers( + this, container); + if (splitContainer != null && splitContainer.isPlaceholderContainer() + && splitContainer.getSecondaryContainer() == container) { + // Remove the placeholder secondary TaskFragment. + containersToRemove.add(container); + } + } + mContainersToFinishOnExit.removeAll(containersToRemove); + for (TaskFragmentContainer container : containersToRemove) { + container.finish(false /* shouldFinishDependent */, presenter, wct, mController); + } + } + boolean isFinished() { return mIsFinished; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index c9f870005eb95f0e4c6295140db658ceeb8f060e..8386131b177dde0449a4af6e5ffa6119cfe064da 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -45,6 +45,7 @@ import androidx.annotation.UiContext; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; +import androidx.window.extensions.core.util.function.Consumer; import androidx.window.util.DataProducer; import java.util.ArrayList; @@ -53,7 +54,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; /** * Reference implementation of androidx.window.extensions.layout OEM interface for use with @@ -82,6 +82,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final Map mConfigurationChangeListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private final Map, Consumer> + mJavaToExtConsumers = new ArrayMap<>(); + private final TaskFragmentOrganizer mTaskFragmentOrganizer; public WindowLayoutComponentImpl(@NonNull Context context, @@ -95,7 +99,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } /** Registers to listen to {@link CommonFoldingFeature} changes */ - public void addFoldingStateChangedCallback(Consumer> consumer) { + public void addFoldingStateChangedCallback( + java.util.function.Consumer> consumer) { synchronized (mLock) { mFoldingFeatureProducer.addDataChangedCallback(consumer); } @@ -109,13 +114,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { */ @Override public void addWindowLayoutInfoListener(@NonNull Activity activity, - @NonNull Consumer consumer) { - addWindowLayoutInfoListener((Context) activity, consumer); + @NonNull java.util.function.Consumer consumer) { + final Consumer extConsumer = consumer::accept; + synchronized (mLock) { + mJavaToExtConsumers.put(consumer, extConsumer); + } + addWindowLayoutInfoListener(activity, extConsumer); + } + + @Override + public void addWindowLayoutInfoListener(@NonNull @UiContext Context context, + @NonNull java.util.function.Consumer consumer) { + final Consumer extConsumer = consumer::accept; + synchronized (mLock) { + mJavaToExtConsumers.put(consumer, extConsumer); + } + addWindowLayoutInfoListener(context, extConsumer); } /** - * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context - * as a parameter. + * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but + * takes a UI Context as a parameter. * * Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all * consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo} @@ -156,6 +175,18 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + @Override + public void removeWindowLayoutInfoListener( + @NonNull java.util.function.Consumer consumer) { + final Consumer extConsumer; + synchronized (mLock) { + extConsumer = mJavaToExtConsumers.remove(consumer); + } + if (extConsumer != null) { + removeWindowLayoutInfoListener(extConsumer); + } + } + /** * Removes a listener no longer interested in receiving updates. * diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java index 2f92a577baa216f159d725e8af2b5465a3dc82da..459ec9f89c4a017460478f9e0de0665e480b8f80 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java @@ -34,9 +34,11 @@ import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.util.Pair; +import android.view.WindowMetrics; import android.window.TaskFragmentInfo; import android.window.WindowContainerToken; +import androidx.window.extensions.core.util.function.Predicate; import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.layout.DisplayFeature; import androidx.window.extensions.layout.FoldingFeature; @@ -107,7 +109,7 @@ public class EmbeddingTestUtils { static SplitRule createSplitRule(@NonNull Activity primaryActivity, @NonNull Intent secondaryIntent, boolean clearTop) { final Pair targetPair = new Pair<>(primaryActivity, secondaryIntent); - return new SplitPairRule.Builder( + return createSplitPairRuleBuilder( activityPair -> false, targetPair::equals, w -> true) @@ -144,7 +146,7 @@ public class EmbeddingTestUtils { @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary, int finishSecondaryWithPrimary, boolean clearTop) { final Pair targetPair = new Pair<>(primaryActivity, secondaryActivity); - return new SplitPairRule.Builder( + return createSplitPairRuleBuilder( targetPair::equals, activityIntentPair -> false, w -> true) @@ -223,4 +225,26 @@ public class EmbeddingTestUtils { displayFeatures.add(foldingFeature); return new WindowLayoutInfo(displayFeatures); } + + static ActivityRule.Builder createActivityBuilder( + @NonNull Predicate activityPredicate, + @NonNull Predicate intentPredicate) { + return new ActivityRule.Builder(activityPredicate, intentPredicate); + } + + static SplitPairRule.Builder createSplitPairRuleBuilder( + @NonNull Predicate> activitiesPairPredicate, + @NonNull Predicate> activityIntentPairPredicate, + @NonNull Predicate windowMetricsPredicate) { + return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate, + windowMetricsPredicate); + } + + static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder( + @NonNull Intent placeholderIntent, @NonNull Predicate activityPredicate, + @NonNull Predicate intentPredicate, + @NonNull Predicate windowMetricsPredicate) { + return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate, + intentPredicate, windowMetricsPredicate); + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 81c39571bffa2b2fa81afea8bf60a6feb6d429e9..0bf0bc85b511171986d91b83164ceaae20d52458 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -34,8 +34,11 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTR import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer; import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds; @@ -432,7 +435,7 @@ public class SplitControllerTest { @Test public void testResolveStartActivityIntent_withoutLaunchingActivity() { final Intent intent = new Intent(); - final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent) + final ActivityRule expandRule = createActivityBuilder(r -> false, i -> i == intent) .setShouldAlwaysExpand(true) .build(); mSplitController.setEmbeddingRules(Collections.singleton(expandRule)); @@ -1170,7 +1173,7 @@ public class SplitControllerTest { @Test public void testHasSamePresentation() { - SplitPairRule splitRule1 = new SplitPairRule.Builder( + SplitPairRule splitRule1 = createSplitPairRuleBuilder( activityPair -> true, activityIntentPair -> true, windowMetrics -> true) @@ -1178,7 +1181,7 @@ public class SplitControllerTest { .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) .build(); - SplitPairRule splitRule2 = new SplitPairRule.Builder( + SplitPairRule splitRule2 = createSplitPairRuleBuilder( activityPair -> true, activityIntentPair -> true, windowMetrics -> true) @@ -1191,7 +1194,7 @@ public class SplitControllerTest { SplitController.haveSamePresentation(splitRule1, splitRule2, new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED))); - splitRule2 = new SplitPairRule.Builder( + splitRule2 = createSplitPairRuleBuilder( activityPair -> true, activityIntentPair -> true, windowMetrics -> true) @@ -1355,7 +1358,7 @@ public class SplitControllerTest { /** Setups a rule to always expand the given intent. */ private void setupExpandRule(@NonNull Intent expandIntent) { - final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals) + final ActivityRule expandRule = createActivityBuilder(r -> false, expandIntent::equals) .setShouldAlwaysExpand(true) .build(); mSplitController.setEmbeddingRules(Collections.singleton(expandRule)); @@ -1363,7 +1366,7 @@ public class SplitControllerTest { /** Setups a rule to always expand the given activity. */ private void setupExpandRule(@NonNull Activity expandActivity) { - final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false) + final ActivityRule expandRule = createActivityBuilder(expandActivity::equals, i -> false) .setShouldAlwaysExpand(true) .build(); mSplitController.setEmbeddingRules(Collections.singleton(expandRule)); @@ -1371,7 +1374,7 @@ public class SplitControllerTest { /** Setups a rule to launch placeholder for the given activity. */ private void setupPlaceholderRule(@NonNull Activity primaryActivity) { - final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT, + final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT, primaryActivity::equals, i -> false, w -> true) .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) .build(); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index 121e81394b2d761dd651f8454efa75a0d4ff0ea9..d286d23da750cd632a038d8e2c363611513c9ef7 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -28,6 +28,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUND import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo; import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds; @@ -76,6 +77,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.extensions.core.util.function.Function; import androidx.window.extensions.layout.WindowLayoutComponentImpl; import androidx.window.extensions.layout.WindowLayoutInfo; @@ -511,7 +513,7 @@ public class SplitPresenterTest { final Activity secondaryActivity = createMockActivity(); final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID); final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID); - final SplitPairRule rule = new SplitPairRule.Builder(pair -> + final SplitPairRule rule = createSplitPairRuleBuilder(pair -> pair.first == mActivity && pair.second == secondaryActivity, pair -> false, metrics -> true) .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) @@ -529,7 +531,7 @@ public class SplitPresenterTest { @Test public void testComputeSplitAttributes() { - final SplitPairRule splitPairRule = new SplitPairRule.Builder( + final SplitPairRule splitPairRule = createSplitPairRuleBuilder( activityPair -> true, activityIntentPair -> true, windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS)) @@ -561,15 +563,36 @@ public class SplitPresenterTest { SplitAttributes.SplitType.RatioSplitType.splitEqually() ) ).build(); + final Function calculator = + params -> splitAttributes; - mController.setSplitAttributesCalculator(params -> { - return splitAttributes; - }); + mController.setSplitAttributesCalculator(calculator); assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties, splitPairRule, null /* minDimensionsPair */)); } + @Test + public void testComputeSplitAttributesOnHingeSplitTypeOnDeviceWithoutFoldingFeature() { + final SplitAttributes hingeSplitAttrs = new SplitAttributes.Builder() + .setSplitType(new SplitAttributes.SplitType.HingeSplitType( + SplitAttributes.SplitType.RatioSplitType.splitEqually())) + .build(); + final SplitPairRule splitPairRule = createSplitPairRuleBuilder( + activityPair -> true, + activityIntentPair -> true, + windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS)) + .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY) + .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) + .setDefaultSplitAttributes(hingeSplitAttrs) + .build(); + final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + doReturn(null).when(mPresenter).getFoldingFeature(any()); + + assertEquals(hingeSplitAttrs, mPresenter.computeSplitAttributes(taskProperties, + splitPairRule, null /* minDimensionsPair */)); + } + private Activity createMockActivity() { final Activity activity = mock(Activity.class); final Configuration activityConfig = new Configuration(); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 7d9d8b0f3a06634ee03b2b6f91b17de5399741d4..78b85e642c13310ad02392ecebce0f8b8d355889 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -154,16 +154,51 @@ public class TaskFragmentContainerTest { null /* pendingAppearedIntent */, taskContainer, mController, null /* pairedPrimaryContainer */); doReturn(container1).when(mController).getContainerWithActivity(mActivity); - final WindowContainerTransaction wct = new WindowContainerTransaction(); // The activity is requested to be reparented, so don't finish it. - container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container0.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); verify(mTransaction, never()).finishActivity(any()); - verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken()); + verify(mPresenter).deleteTaskFragment(mTransaction, container0.getTaskFragmentToken()); verify(mController).removeContainer(container0); } + @Test + public void testFinish_alwaysFinishPlaceholder() { + // Register container1 as a placeholder + final TaskContainer taskContainer = createTestTaskContainer(); + final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity, + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); + final TaskFragmentInfo info0 = createMockTaskFragmentInfo(container0, mActivity); + container0.setInfo(mTransaction, info0); + final Activity placeholderActivity = createMockActivity(); + final TaskFragmentContainer container1 = new TaskFragmentContainer(placeholderActivity, + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); + final TaskFragmentInfo info1 = createMockTaskFragmentInfo(container1, placeholderActivity); + container1.setInfo(mTransaction, info1); + final SplitAttributes splitAttributes = new SplitAttributes.Builder().build(); + final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder(new Intent(), + mActivity::equals, (java.util.function.Predicate) i -> false, + (java.util.function.Predicate) w -> true) + .setDefaultSplitAttributes(splitAttributes) + .build(); + mController.registerSplit(mTransaction, container0, mActivity, container1, rule, + splitAttributes); + + // The placeholder TaskFragment should be finished even if the primary is finished with + // shouldFinishDependent = false. + container0.finish(false /* shouldFinishDependent */, mPresenter, mTransaction, mController); + + assertTrue(container0.isFinished()); + assertTrue(container1.isFinished()); + verify(mPresenter).deleteTaskFragment(mTransaction, container0.getTaskFragmentToken()); + verify(mPresenter).deleteTaskFragment(mTransaction, container1.getTaskFragmentToken()); + verify(mController).removeContainer(container0); + verify(mController).removeContainer(container1); + } + @Test public void testSetInfo() { final TaskContainer taskContainer = createTestTaskContainer(); @@ -493,8 +528,6 @@ public class TaskFragmentContainerTest { final TaskFragmentContainer tf1 = new TaskFragmentContainer( null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, null /* pairedPrimaryTaskFragment */); - taskContainer.mContainers.add(tf0); - taskContainer.mContainers.add(tf1); // When tf2 is created with using tf0 as pairedPrimaryContainer, tf2 should be inserted // right above tf0. @@ -505,6 +538,26 @@ public class TaskFragmentContainerTest { assertEquals(2, taskContainer.indexOf(tf1)); } + @Test + public void testNewContainerWithPairedPendingAppearedActivity() { + final TaskContainer taskContainer = createTestTaskContainer(); + final TaskFragmentContainer tf0 = new TaskFragmentContainer( + createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + final TaskFragmentContainer tf1 = new TaskFragmentContainer( + null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + + // When tf2 is created with pendingAppearedActivity, tf2 should be inserted below any + // TaskFragment without any Activity. + final TaskFragmentContainer tf2 = new TaskFragmentContainer( + createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + assertEquals(0, taskContainer.indexOf(tf0)); + assertEquals(1, taskContainer.indexOf(tf2)); + assertEquals(2, taskContainer.indexOf(tf1)); + } + @Test public void testIsVisible() { final TaskContainer taskContainer = createTestTaskContainer(); diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar new file mode 100644 index 0000000000000000000000000000000000000000..96ff840b984beaa757a2472732f7708104e1908a Binary files /dev/null and b/libs/WindowManager/Jetpack/window-extensions-core-release.aar differ diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar index 84ab4487feeec395ea3e30d21ef6669f6022f676..c3b6916121d057043f2be78037c22abd517101e7 100644 Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index f615ad6e671b246a675a98f3d2238f007a81dcb6..c7c94246b96a01bf7629165244af1d4175007c0d 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -47,7 +47,9 @@ filegroup { "src/com/android/wm/shell/sysui/ShellSharedConstants.java", "src/com/android/wm/shell/common/TransactionPool.java", "src/com/android/wm/shell/animation/Interpolators.java", + "src/com/android/wm/shell/pip/PipContentOverlay.java", "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java", + "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java", ], path: "src", } diff --git a/libs/WindowManager/Shell/res/color-night/taskbar_background.xml b/libs/WindowManager/Shell/res/color-night/taskbar_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..01df006f1bd294c1c80eb21cbbb6b19bd3cded57 --- /dev/null +++ b/libs/WindowManager/Shell/res/color-night/taskbar_background.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/split_divider_background.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml similarity index 83% rename from libs/WindowManager/Shell/res/color/split_divider_background.xml rename to libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml index 049980803ee3d771e9f56d56e1f94740582a62c6..a3ca74fac4e6c27c06920b42bc956b7d7ab5ae81 100644 --- a/libs/WindowManager/Shell/res/color/split_divider_background.xml +++ b/libs/WindowManager/Shell/res/color/letterbox_restart_button_background_ripple.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml new file mode 100644 index 0000000000000000000000000000000000000000..a3ca74fac4e6c27c06920b42bc956b7d7ab5ae81 --- /dev/null +++ b/libs/WindowManager/Shell/res/color/letterbox_restart_dismiss_button_background_ripple.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml index b3d2602991062b07ad928101f56def4cbdf29598..876ee02a8adfcb2c87854468ca8ddf6302f389fe 100644 --- a/libs/WindowManager/Shell/res/color/taskbar_background.xml +++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml @@ -16,5 +16,5 @@ --> - + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/unfold_background.xml b/libs/WindowManager/Shell/res/color/unfold_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..e33eb126012d5bb985355e41ad6d0f67585aaa53 --- /dev/null +++ b/libs/WindowManager/Shell/res/color/unfold_background.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/caption_close_button.xml b/libs/WindowManager/Shell/res/drawable/caption_close_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..e258564c70f7f4041520ba805e95d8f7ff15c9fd --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_close_button.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml b/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..166552dcb9e831672d46340ac8cd2fb191ac2bf3 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_collapse_menu_button.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml b/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml new file mode 100644 index 0000000000000000000000000000000000000000..6114ad6e277a6fc6292f8fd1f2b456d09ed84279 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_decor_title.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml b/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..7c86888f52263d4081700044207988f7a4e7ca69 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_screenshot_button.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/caption_select_button.xml b/libs/WindowManager/Shell/res/drawable/caption_select_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..8c60c840717431fdca387b740cc88f3ac5bafe15 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_select_button.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml index c9f262398f683065073996690fd66683705e5810..27e0b184f4275fd765813a562f480dfbee7d00dc 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml @@ -17,9 +17,10 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="@color/decor_button_dark_color"> + android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/> diff --git a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml index 0bcaa530dc8030602771bbd33ab26eb2ee9165be..91edbf1a7bd4d4bca41e55df932979df06e43aa5 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml @@ -14,11 +14,11 @@ ~ limitations under the License. --> + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" + android:tint="@color/decor_button_dark_color"> diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml index 416287d2cbb334ae28d24e08ef4ccc51dcbdd9a8..c6e634c6622c3f6af3dae7996ef272383b062d85 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml @@ -17,5 +17,6 @@ - + + diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml index 416287d2cbb334ae28d24e08ef4ccc51dcbdd9a8..ef300604226186f007edaea62ce869eacba64b02 100644 --- a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_title.xml @@ -15,7 +15,8 @@ ~ limitations under the License. --> - diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml index 42572d64b96fde0ebe925a8071f61274096861f8..a2699681e6564dcd75901d1dff3aee513bf6883e 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml @@ -14,7 +14,30 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml new file mode 100644 index 0000000000000000000000000000000000000000..1f125148775dd9350f785339914874986bd1b8ad --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_button_background_ripple.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..c247c6e4c8cfb81b0f134bd2bd9a1b923cfa3c04 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_button.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml new file mode 100644 index 0000000000000000000000000000000000000000..4f97e2c7ea0d1b2d1b64be9631661e0d6aa3ee4a --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_checked.xml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml new file mode 100644 index 0000000000000000000000000000000000000000..bb14d1961e818de07a6ccef52bbab7ebae2bbc5a --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_checkbox_unchecked.xml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml similarity index 73% rename from libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml rename to libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml index 0d8811357c058c79c41c673df8ba713e0075994d..e3c18a2db66feb6643d13b5490e607f393faad1f 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dialog_background.xml @@ -1,6 +1,6 @@ - - + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml new file mode 100644 index 0000000000000000000000000000000000000000..3aa0981e45aaaefd8aae7423a39bc59ca2b0032c --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_dismiss_button_background_ripple.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml new file mode 100644 index 0000000000000000000000000000000000000000..5053971a17d3a1cbd786e14d0c0633807cc52070 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_header_ic_arrows.xml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6e0172af1df2a83951ebf9f693bd0b1c95e4ad9 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/letterbox_restart_ic_arrows.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml new file mode 100644 index 0000000000000000000000000000000000000000..029d83881165644befee5e1e1846565c333e5f70 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_left_hand.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml new file mode 100644 index 0000000000000000000000000000000000000000..592f899d2ecce881bc1e63b5106ff29dbda852c8 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/reachability_education_ic_right_hand.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml index 29945937788bb0a00e0e95e95ea8255d0a3b64e6..b3f8e801bac4efe2d32e1c7d24af6aacaa7cf026 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml +++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml @@ -25,12 +25,10 @@ android:fillAlpha="0.8" android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/> + android:translateX="12" + android:translateY="12"> + android:fillColor="@color/compat_controls_text" + android:pathData="M3,21V15H5V17.6L8.1,14.5L9.5,15.9L6.4,19H9V21ZM15,21V19H17.6L14.5,15.9L15.9,14.5L19,17.6V15H21V21ZM8.1,9.5 L5,6.4V9H3V3H9V5H6.4L9.5,8.1ZM15.9,9.5 L14.5,8.1 17.6,5H15V3H21V9H19V6.4Z"/> diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decor.xml b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml new file mode 100644 index 0000000000000000000000000000000000000000..f3d219872001af05fe93f132ccdb4c3a8e24aeda --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/caption_window_decor.xml @@ -0,0 +1,56 @@ + + + +