diff options
Diffstat (limited to 'core/java/android')
54 files changed, 1567 insertions, 242 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index cebe6e1211e0..90b80e73c323 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -309,7 +309,7 @@ public abstract class AccessibilityService extends Service { * Name under which an AccessibilityService component publishes information * about itself. This meta-data must reference an XML resource containing an * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> - * tag. This is a a sample XML file configuring an accessibility service: + * tag. This is a sample XML file configuring an accessibility service: * <pre> <accessibility-service * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" * android:packageNames="foo.bar, foo.baz" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index dc52c52cca1f..f5b0b592e6a7 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1547,7 +1547,9 @@ public class Activity extends ContextThemeWrapper * had previously been frozen by {@link #onSaveInstanceState}. * * <p>This method is called between {@link #onStart} and - * {@link #onPostCreate}. + * {@link #onPostCreate}. This method is called only when recreating + * an activity; the method isn't invoked if {@link #onStart} is called for + * any other reason.</p> * * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. * diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 17368b789645..91b98c71a613 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -925,7 +925,7 @@ public class ActivityManager { * (which tends to consume a lot more RAM). * @hide */ - @UnsupportedAppUsage + @TestApi static public boolean isHighEndGfx() { return !isLowRamDeviceStatic() && !RoSystemProperties.CONFIG_AVOID_GFX_ACCEL diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 8508c2c95666..1725db046a95 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -120,17 +120,6 @@ public abstract class ActivityManagerInternal { public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi); /** - * Sets if the given pid is currently running a remote animation, which is taken a signal for - * determining oom adjustment and scheduling behavior. - * - * @param pid The pid we are setting overlay UI for. - * @param runningRemoteAnimation True if the process is running a remote animation, false - * otherwise. - * @see RemoteAnimationAdapter - */ - public abstract void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation); - - /** * Called after the network policy rules are updated by * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and * {@param procStateSeq}. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d780b09bc7f1..1b515737c479 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1005,8 +1005,7 @@ public final class ActivityThread extends ClientTransactionHandler { boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, - ContentCaptureOptions contentCaptureOptions, - long[] disabledCompatChanges) { + ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) { if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1052,6 +1051,8 @@ public final class ActivityThread extends ClientTransactionHandler { data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; data.buildSerial = buildSerial; + data.autofillOptions = autofillOptions; + data.contentCaptureOptions = contentCaptureOptions; data.disabledCompatChanges = disabledCompatChanges; sendMessage(H.BIND_APPLICATION, data); } @@ -6133,7 +6134,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (data.trackAllocation) { DdmVmInternal.enableRecentAllocations(true); } - // Note when this process has started. Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 4771f9f6ad04..3bf659b663b0 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -120,6 +120,7 @@ public class ActivityView extends ViewGroup { mActivityTaskManager = ActivityTaskManager.getService(); mSurfaceView = new SurfaceView(context); + mSurfaceView.setAlpha(0f); mSurfaceCallback = new SurfaceCallback(); mSurfaceView.getHolder().addCallback(mSurfaceCallback); addView(mSurfaceView); @@ -347,6 +348,16 @@ public class ActivityView extends ViewGroup { } @Override + public void setAlpha(float alpha) { + mSurfaceView.setAlpha(alpha); + } + + @Override + public float getAlpha() { + return mSurfaceView.getAlpha(); + } + + @Override public boolean gatherTransparentRegion(Region region) { // The tap exclude region may be affected by any view on top of it, so we detect the // possible change by monitoring this function. diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 0df3bbe42c7e..650147b45bd2 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -170,6 +170,14 @@ oneway interface ITaskStackListener { */ void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo); + /* + * Called when contents are drawn for the first time on a display which can only contain one + * task. + * + * @param displayId the id of the display on which contents are drawn. + */ + void onSingleTaskDisplayDrawn(int displayId); + /** * Called when a task is reparented to a stack on a different display. * diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 789351e0d157..ceadd8510e44 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3386,11 +3386,7 @@ public class Notification implements Parcelable */ private int mCachedContrastColor = COLOR_INVALID; private int mCachedContrastColorIsFor = COLOR_INVALID; - /** - * Caches a ambient version of {@link #mCachedAmbientColorIsFor}. - */ - private int mCachedAmbientColor = COLOR_INVALID; - private int mCachedAmbientColorIsFor = COLOR_INVALID; + /** * A neutral color color that can be used for icons. */ @@ -5441,26 +5437,14 @@ public class Notification implements Parcelable /** * Construct a RemoteViews for the display in public contexts like on the lockscreen. * + * @param isLowPriority is this notification low priority * @hide */ @UnsupportedAppUsage - public RemoteViews makePublicContentView() { - return makePublicView(false /* ambient */); - } - - /** - * Construct a RemoteViews for the display in public contexts like on the lockscreen. - * - * @hide - */ - public RemoteViews makePublicAmbientNotification() { - return makePublicView(true /* ambient */); - } - - private RemoteViews makePublicView(boolean ambient) { + public RemoteViews makePublicContentView(boolean isLowPriority) { if (mN.publicVersion != null) { final Builder builder = recoverBuilder(mContext, mN.publicVersion); - return ambient ? builder.makeAmbientNotification() : builder.createContentView(); + return builder.createContentView(); } Bundle savedBundle = mN.extras; Style style = mStyle; @@ -5484,7 +5468,11 @@ public class Notification implements Parcelable } mN.extras = publicExtras; RemoteViews view; - view = makeNotificationHeader(); + StandardTemplateParams params = mParams.reset().fillTextsFrom(this); + if (isLowPriority) { + params.forceDefaultColor(); + } + view = makeNotificationHeader(params); view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true); mN.extras = savedBundle; mN.mLargeIcon = largeIcon; @@ -8118,8 +8106,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { - RemoteViews expanded = makeMediaBigContentView(); - return expanded != null ? expanded : makeMediaContentView(); + return makeMediaContentView(); } /** @hide */ diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index d9e8763d7f7b..28413be29a1d 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -433,6 +433,9 @@ public class StatusBarManager { private boolean mNotificationPeeking; private boolean mRecents; private boolean mSearch; + private boolean mSystemIcons; + private boolean mClock; + private boolean mNotificationIcons; /** @hide */ public DisableInfo(int flags1, int flags2) { @@ -441,6 +444,9 @@ public class StatusBarManager { mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0; mRecents = (flags1 & DISABLE_RECENT) != 0; mSearch = (flags1 & DISABLE_SEARCH) != 0; + mSystemIcons = (flags1 & DISABLE_SYSTEM_INFO) != 0; + mClock = (flags1 & DISABLE_CLOCK) != 0; + mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0; } /** @hide */ @@ -527,6 +533,48 @@ public class StatusBarManager { } /** + * @return {@code true} if system icons are disabled + * + * @hide + */ + public boolean areSystemIconsDisabled() { + return mSystemIcons; + } + + /** * @hide */ + public void setSystemIconsDisabled(boolean disabled) { + mSystemIcons = disabled; + } + + /** + * @return {@code true} if the clock icon is disabled + * + * @hide + */ + public boolean isClockDisabled() { + return mClock; + } + + /** * @hide */ + public void setClockDisabled(boolean disabled) { + mClock = disabled; + } + + /** + * @return {@code true} if notification icons are disabled + * + * @hide + */ + public boolean areNotificationIconsDisabled() { + return mNotificationIcons; + } + + /** * @hide */ + public void setNotificationIconsDisabled(boolean disabled) { + mNotificationIcons = disabled; + } + + /** * @return {@code true} if no components are disabled (default state) * * @hide @@ -535,7 +583,7 @@ public class StatusBarManager { @TestApi public boolean areAllComponentsEnabled() { return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents - && !mSearch; + && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons; } /** @hide */ @@ -545,6 +593,9 @@ public class StatusBarManager { mNotificationPeeking = false; mRecents = false; mSearch = false; + mSystemIcons = false; + mClock = false; + mNotificationIcons = false; } /** @@ -554,7 +605,7 @@ public class StatusBarManager { */ public boolean areAllComponentsDisabled() { return mStatusBarExpansion && mNavigateHome && mNotificationPeeking - && mRecents && mSearch; + && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons; } /** @hide */ @@ -564,6 +615,9 @@ public class StatusBarManager { mNotificationPeeking = true; mRecents = true; mSearch = true; + mSystemIcons = true; + mClock = true; + mNotificationIcons = true; } @NonNull @@ -577,6 +631,9 @@ public class StatusBarManager { .append(mNotificationPeeking ? "disabled" : "enabled"); sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled"); sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled"); + sb.append(" mSystemIcons=").append(mSystemIcons ? "disabled" : "enabled"); + sb.append(" mClock=").append(mClock ? "disabled" : "enabled"); + sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled"); return sb.toString(); @@ -597,6 +654,9 @@ public class StatusBarManager { if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS; if (mRecents) disable1 |= DISABLE_RECENT; if (mSearch) disable1 |= DISABLE_SEARCH; + if (mSystemIcons) disable1 |= DISABLE_SYSTEM_INFO; + if (mClock) disable1 |= DISABLE_CLOCK; + if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS; return new Pair<Integer, Integer>(disable1, disable2); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 73fc7a3db6c7..b63feb590ad1 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -176,6 +176,10 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override + public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException { + } + + @Override public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException { } } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 099ae291c8f4..e78fb7f00797 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -339,6 +339,8 @@ public final class Sensor { * for {@link #TYPE_STEP_COUNTER} instead. It is defined as a * {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER} sensor. * <p> + * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. + * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_STEP_DETECTOR = 18; @@ -384,8 +386,6 @@ public final class Sensor { * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't * use the gyroscope. However, it is more noisy and will work best outdoors. * <p> - * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. - * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 6c497d47c645..d8110f33d723 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -104,9 +104,17 @@ public class BiometricManager { */ @RequiresPermission(USE_BIOMETRIC) public @BiometricError int canAuthenticate() { + return canAuthenticate(mContext.getUserId()); + } + + /** + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public @BiometricError int canAuthenticate(int userId) { if (mService != null) { try { - return mService.canAuthenticate(mContext.getOpPackageName()); + return mService.canAuthenticate(mContext.getOpPackageName(), userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -121,6 +129,25 @@ public class BiometricManager { } /** + * @hide + * @param userId + * @return + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public boolean hasEnrolledBiometrics(int userId) { + if (mService != null) { + try { + return mService.hasEnrolledBiometrics(userId); + } catch (RemoteException e) { + Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e); + return false; + } + } else { + return false; + } + } + + /** * Listens for changes to biometric eligibility on keyguard from user settings. * @param callback * @hide diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl index d22e7e295b77..62d727c080e3 100644 --- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl +++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl @@ -22,5 +22,5 @@ import android.hardware.biometrics.BiometricSourceType; * @hide */ oneway interface IBiometricEnabledOnKeyguardCallback { - void onChanged(in BiometricSourceType type, boolean enabled); + void onChanged(in BiometricSourceType type, boolean enabled, int userId); }
\ No newline at end of file diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 90d4921c3c18..f0a0b2f0235f 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -40,7 +40,10 @@ interface IBiometricService { void cancelAuthentication(IBinder token, String opPackageName); // Checks if biometrics can be used. - int canAuthenticate(String opPackageName); + int canAuthenticate(String opPackageName, int userId); + + // Checks if any biometrics are enrolled. + boolean hasEnrolledBiometrics(int userId); // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index c45b8ed52187..3e995b624112 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -48,7 +48,8 @@ public class AmbientDisplayConfiguration { return pulseOnNotificationEnabled(user) || pulseOnLongPressEnabled(user) || alwaysOnEnabled(user) - || wakeScreenGestureEnabled(user) + || wakeLockScreenGestureEnabled(user) + || wakeDisplayGestureEnabled(user) || pickupGestureEnabled(user) || tapGestureEnabled(user) || doubleTapGestureEnabled(user); @@ -105,8 +106,14 @@ public class AmbientDisplayConfiguration { } /** {@hide} */ - public boolean wakeScreenGestureEnabled(int user) { - return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user) + public boolean wakeLockScreenGestureEnabled(int user) { + return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user) + && wakeScreenGestureAvailable(); + } + + /** {@hide} */ + public boolean wakeDisplayGestureEnabled(int user) { + return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, user) && wakeScreenGestureAvailable(); } diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 8596af107ac1..12b285a0f0ab 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -262,7 +262,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public void enroll(byte[] token, CancellationSignal cancel, + public void enroll(int userId, byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); @@ -281,7 +281,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan try { mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enroll"); - mService.enroll(mToken, token, mServiceReceiver, + mService.enroll(userId, mToken, token, mServiceReceiver, mContext.getOpPackageName(), disabledFeatures); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); @@ -339,12 +339,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public void setFeature(int feature, boolean enabled, byte[] token, + public void setFeature(int userId, int feature, boolean enabled, byte[] token, SetFeatureCallback callback) { if (mService != null) { try { mSetFeatureCallback = callback; - mService.setFeature(feature, enabled, token, mServiceReceiver); + mService.setFeature(userId, feature, enabled, token, mServiceReceiver, + mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -355,11 +356,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public void getFeature(int feature, GetFeatureCallback callback) { + public void getFeature(int userId, int feature, GetFeatureCallback callback) { if (mService != null) { try { mGetFeatureCallback = callback; - mService.getFeature(feature, mServiceReceiver); + mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -414,7 +415,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan try { mRemovalCallback = callback; mRemovalFace = face; - mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver); + mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver, + mContext.getOpPackageName()); } catch (RemoteException e) { Log.w(TAG, "Remote exception in remove: ", e); if (callback != null) { @@ -637,7 +639,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); - return null; + return ""; } /** diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 601be7595581..b6a0afbf716c 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -50,14 +50,15 @@ interface IFaceService { int callingUid, int callingPid, int callingUserId, boolean fromClient); // Start face enrollment - void enroll(IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, + void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, String opPackageName, in int [] disabledFeatures); // Cancel enrollment in progress void cancelEnrollment(IBinder token); // Any errors resulting from this call will be returned to the listener - void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver); + void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver, + String opPackageName); // Rename the face specified by faceId to the given name void rename(int faceId, String name); @@ -98,10 +99,10 @@ interface IFaceService { // Enumerate all faces void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver); - void setFeature(int feature, boolean enabled, in byte [] token, - IFaceServiceReceiver receiver); + void setFeature(int userId, int feature, boolean enabled, in byte [] token, + IFaceServiceReceiver receiver, String opPackageName); - void getFeature(int feature, IFaceServiceReceiver receiver); + void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName); void userActivity(); } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index ae421a4991a4..87c7118c0ed8 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -48,6 +48,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -481,17 +482,39 @@ public class NetworkTemplate implements Parcelable { * For example, given an incoming template matching B, and the currently * active merge set [A,B], we'd return a new template that primarily matches * A, but also matches B. + * TODO: remove and use {@link #normalize(NetworkTemplate, List)}. */ @UnsupportedAppUsage public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { - if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { - // Requested template subscriber is part of the merge group; return - // a template that matches all merged subscribers. - return new NetworkTemplate(template.mMatchRule, merged[0], merged, - template.mNetworkId); - } else { - return template; + return normalize(template, Arrays.<String[]>asList(merged)); + } + + /** + * Examine the given template and normalize if it refers to a "merged" + * mobile subscriber. We pick the "lowest" merged subscriber as the primary + * for key purposes, and expand the template to match all other merged + * subscribers. + * + * There can be multiple merged subscriberIds for multi-SIM devices. + * + * <p> + * For example, given an incoming template matching B, and the currently + * active merge set [A,B], we'd return a new template that primarily matches + * A, but also matches B. + */ + public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) { + if (!template.isMatchRuleMobile()) return template; + + for (String[] merged : mergedList) { + if (ArrayUtils.contains(merged, template.mSubscriberId)) { + // Requested template subscriber is part of the merge group; return + // a template that matches all merged subscribers. + return new NetworkTemplate(template.mMatchRule, merged[0], merged, + template.mNetworkId); + } } + + return template; } @UnsupportedAppUsage diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index e8b3ca6cb7ae..1456ff7e6c5e 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -16,6 +16,7 @@ package android.os; +import android.media.AudioAttributes; import android.os.VibrationEffect; /** {@hide} */ @@ -23,8 +24,8 @@ interface IVibratorService { boolean hasVibrator(); boolean hasAmplitudeControl(); - void vibrate(int uid, String opPkg, in VibrationEffect effect, int usageHint, String reason, - IBinder token); + void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes, + String reason, IBinder token); void cancelVibrate(IBinder token); } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 53298d8d8aed..48fc2a6bf449 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -31,6 +31,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.provider.Settings; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; import android.text.format.DateFormat; @@ -56,10 +58,12 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -87,11 +91,14 @@ public class RecoverySystem { private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500; private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s - private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s - private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s + private static final long DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = + 45000L; // 45 s + private static final long MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 15000L; // 15 s + private static final long MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 90000L; // 90 s + /** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */ private static final File RECOVERY_DIR = new File("/cache/recovery"); private static final File LOG_FILE = new File(RECOVERY_DIR, "log"); @@ -99,9 +106,14 @@ public class RecoverySystem { private static final String LAST_PREFIX = "last_"; private static final String ACTION_EUICC_FACTORY_RESET = "com.android.internal.action.EUICC_FACTORY_RESET"; + private static final String ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS = + "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS"; - /** used in {@link #wipeEuiccData} as package name of callback intent */ - private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android"; + /** + * Used in {@link #wipeEuiccData} & {@link #removeEuiccInvisibleSubs} as package name of + * callback intent. + */ + private static final String PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK = "android"; /** * The recovery image uses this file to identify the location (i.e. blocks) @@ -754,8 +766,11 @@ public class RecoverySystem { // Block until the ordered broadcast has completed. condition.block(); + EuiccManager euiccManager = context.getSystemService(EuiccManager.class); if (wipeEuicc) { - wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK); + wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); + } else { + removeEuiccInvisibleSubs(context, euiccManager); } String shutdownArg = null; @@ -851,6 +866,110 @@ public class RecoverySystem { return false; } + private static void removeEuiccInvisibleSubs( + Context context, EuiccManager euiccManager) { + ContentResolver cr = context.getContentResolver(); + if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { + // If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles, + // as there's nothing to be removed. + Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned."); + return; + } else if (euiccManager == null || !euiccManager.isEnabled()) { + Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available."); + return; + } + SubscriptionManager subscriptionManager = + context.getSystemService(SubscriptionManager.class); + List<SubscriptionInfo> availableSubs = + subscriptionManager.getAvailableSubscriptionInfoList(); + if (availableSubs == null || availableSubs.isEmpty()) { + Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found."); + return; + } + List<SubscriptionInfo> invisibleSubs = new ArrayList<>(); + for (SubscriptionInfo sub : availableSubs) { + if (sub.isEmbedded() && !subscriptionManager.isSubscriptionVisible(sub)) { + invisibleSubs.add(sub); + } + } + removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager); + } + + private static boolean removeEuiccInvisibleSubs( + Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) { + if (subscriptionInfos == null || subscriptionInfos.isEmpty()) { + Log.i(TAG, "There are no eUICC invisible profiles needed to be removed."); + return true; + } + CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size()); + final AtomicInteger removedSubsCount = new AtomicInteger(0); + + BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) { + if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { + int detailedCode = intent.getIntExtra( + EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0); + Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = " + + detailedCode); + } else { + Log.e(TAG, "Successfully remove euicc opportunistic profile."); + removedSubsCount.incrementAndGet(); + } + removeSubsLatch.countDown(); + } + } + }; + + Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); + intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); + PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( + context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); + HandlerThread euiccHandlerThread = + new HandlerThread("euiccRemovingSubsReceiverThread"); + euiccHandlerThread.start(); + Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); + context.getApplicationContext() + .registerReceiver( + removeEuiccSubsReceiver, intentFilter, null, euiccHandler); + for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { + Log.i( + TAG, + "Remove invisible subscription " + subscriptionInfo.getSubscriptionId() + + " from card " + subscriptionInfo.getCardId()); + euiccManager.createForCardId(subscriptionInfo.getCardId()) + .deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent); + } + try { + long waitingTimeMillis = Settings.Global.getLong( + context.getContentResolver(), + Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, + DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS); + if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { + waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; + } else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { + waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; + } + if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) { + Log.e(TAG, "Timeout removing invisible euicc profiles."); + return false; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + Log.e(TAG, "Removing invisible euicc profiles interrupted", e); + return false; + } finally { + context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver); + if (euiccHandlerThread != null) { + euiccHandlerThread.quit(); + } + } + return removedSubsCount.get() == subscriptionInfos.size(); + } + /** {@hide} */ public static void rebootPromptAndWipeUserData(Context context, String reason) throws IOException { diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java index 7df4b5d8f63b..57a88012a31a 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -25,8 +25,6 @@ import android.system.OsConstants; import dalvik.system.VMRuntime; -import libcore.io.IoUtils; - import java.io.Closeable; import java.io.FileDescriptor; import java.nio.ByteBuffer; @@ -64,7 +62,7 @@ public final class SharedMemory implements Parcelable, Closeable { mMemoryRegistration = new MemoryRegistration(mSize); mCleaner = Cleaner.create(mFileDescriptor, - new Closer(mFileDescriptor.getInt$(), mMemoryRegistration)); + new Closer(mFileDescriptor, mMemoryRegistration)); } /** @@ -261,9 +259,6 @@ public final class SharedMemory implements Parcelable, Closeable { mCleaner.clean(); mCleaner = null; } - - // Cleaner.clean doesn't clear the value of the file descriptor. - mFileDescriptor.setInt$(-1); } @Override @@ -295,24 +290,19 @@ public final class SharedMemory implements Parcelable, Closeable { * Cleaner that closes the FD */ private static final class Closer implements Runnable { - // This is a copy of the FileDescriptor we're attached to, in order to avoid a reference - // cycle. private FileDescriptor mFd; private MemoryRegistration mMemoryReference; - private Closer(int fd, MemoryRegistration memoryReference) { - mFd = new FileDescriptor(); - mFd.setInt$(fd); - IoUtils.setFdOwner(mFd, this); - + private Closer(FileDescriptor fd, MemoryRegistration memoryReference) { + mFd = fd; mMemoryReference = memoryReference; } @Override public void run() { - IoUtils.closeQuietly(mFd); - mFd = null; - + try { + Os.close(mFd); + } catch (ErrnoException e) { /* swallow error */ } mMemoryReference.release(); mMemoryReference = null; } diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index 4af514abfd69..a5188e7cd58d 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -77,16 +77,12 @@ public class SystemVibrator extends Vibrator { return; } try { - mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken); + mService.vibrate(uid, opPkg, effect, attributes, reason, mToken); } catch (RemoteException e) { Log.w(TAG, "Failed to vibrate.", e); } } - private static int usageForAttributes(AudioAttributes attributes) { - return attributes != null ? attributes.getUsage() : AudioAttributes.USAGE_UNKNOWN; - } - @Override public void cancel() { if (mService == null) { diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 81e1eb99336b..af3a16c987e6 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -870,8 +870,8 @@ public final class ContactsContract { protected interface ContactOptionsColumns { /** * The number of times a contact has been contacted. - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. For - * more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page.</p> * <P>Type: INTEGER</P> @@ -885,8 +885,8 @@ public final class ContactsContract { /** * The last time a contact was contacted. - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. For - * more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page.</p> * <P>Type: INTEGER</P> @@ -1691,10 +1691,10 @@ public final class ContactsContract { * TIMES_CONTACTED field is incremented by 1 and the LAST_TIME_CONTACTED * field is populated with the current system time. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this method is obsolete. For - * more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> - * page. + * page.</p> * * @param resolver the ContentResolver to use * @param contactId the person who was contacted @@ -1730,8 +1730,8 @@ public final class ContactsContract { * Frequent contacts are no longer included in the result as of * Android version {@link android.os.Build.VERSION_CODES#Q}. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts - * results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this + * field doesn't sort results based on contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -1745,8 +1745,8 @@ public final class ContactsContract { * Android version {@link android.os.Build.VERSION_CODES#Q}. * This URI always returns an empty cursor. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts - * results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this + * field doesn't sort results based on contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -1760,8 +1760,8 @@ public final class ContactsContract { * various parts of the contact name. The filter argument should be passed * as an additional path segment after this URI. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts - * results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this + * field doesn't sort results based on contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -4292,10 +4292,10 @@ public final class ContactsContract { * Android version {@link android.os.Build.VERSION_CODES#Q}. * This column always contains 0. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. - * For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> - * page. + * page.</p> */ @Deprecated public static final String LAST_TIME_USED = "last_time_used"; @@ -4306,10 +4306,10 @@ public final class ContactsContract { * Android version {@link android.os.Build.VERSION_CODES#Q}. * This column always contains 0. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. - * For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> - * page. + * page.</p> */ @Deprecated public static final String TIMES_USED = "times_used"; @@ -5259,8 +5259,8 @@ public final class ContactsContract { /** * The content:// style URI for this table. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this + * field doesn't sort results based on contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. * @@ -5277,8 +5277,8 @@ public final class ContactsContract { /** * <p>URI used for the "enterprise caller-id".</p> * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this + * field doesn't sort results based on contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. * @@ -6079,8 +6079,8 @@ public final class ContactsContract { * to display names as well as phone numbers. The filter argument should be passed * as an additional path segment after this URI. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>This field deosn't sort results based on contacts + * frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -6092,8 +6092,9 @@ public final class ContactsContract { * same columns. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in * parameters, otherwise it will throw IllegalArgumentException. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts - * results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field doesn't sort results based on contacts frequency. For more information, + * see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -6360,8 +6361,9 @@ public final class ContactsContract { * as an additional path segment after this URI. * </p> * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts - * results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field doesn't sort results based on contacts frequency. For more information, + * see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page.</p> * @@ -6383,8 +6385,9 @@ public final class ContactsContract { * same columns. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in * parameters, otherwise it will throw IllegalArgumentException. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field doesn't sort results based on contacts frequency. For more information, + * see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -7602,8 +7605,8 @@ public final class ContactsContract { * <p>Similar to {@link Phone#CONTENT_FILTER_URI}, but allows users to filter callable * data. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>This field no longer sorts results based on + * contacts frequency. For more information, see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -7615,8 +7618,9 @@ public final class ContactsContract { * callable data. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in * parameters, otherwise it will throw IllegalArgumentException. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field doesn't sort results based on contacts frequency. For more information, + * see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page.</p> */ @@ -7646,8 +7650,9 @@ public final class ContactsContract { * <p>The content:// style URI for these data items, which allows for a query parameter * to be appended onto the end to filter for data items matching the query. * - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer - * sorts results based on contacts frequency. For more information, see the + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field doesn't sort results based on contacts frequency. For more information, + * see the * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> * page. */ @@ -8298,15 +8303,14 @@ public final class ContactsContract { } /** - * <p class="caution"><b>Caution: </b>As of January 7, 2019, this class is obsolete. For - * more information, see the - * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> - * page. - * </p> * <p> * API allowing applications to send usage information for each {@link Data} row to the * Contacts Provider. Applications can also clear all usage information. * </p> + * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, + * this field is obsolete, regardless of Android version. For more information, see the + * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a> + * page.</p> * <p> * With the feedback, Contacts Provider may return more contextually appropriate results for * Data listing, typically supplied with diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index e30ba38c127f..ea50ae810535 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -336,6 +336,29 @@ public final class DeviceConfig { @TestApi String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive"; + + /** + * The minimum duration between gesture exclusion logging for a given window in + * milliseconds. + * + * Events that happen in-between will be silently dropped. + * + * A non-positive value disables logging. + * + * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER + * @hide + */ + String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS = + "system_gesture_exclusion_log_debounce_millis"; + + /** + * Key for controlling which packages are explicitly blocked from running at refresh rates + * higher than 60hz. + * + * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER + * @hide + */ + String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist"; } private static final Object sLock = new Object(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 383d8dbce791..f92be195229e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -88,6 +88,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; +import android.view.Display; import android.view.inputmethod.InputMethodSystemProperty; import com.android.internal.annotations.GuardedBy; @@ -4073,7 +4074,7 @@ public final class Settings { * preference, this rotation value will be used. Must be one of the * {@link android.view.Surface#ROTATION_0 Surface rotation constants}. * - * @see android.view.Display#getRotation + * @see Display#getRotation */ public static final String USER_ROTATION = "user_rotation"; @@ -6321,13 +6322,15 @@ public final class Settings { "lock_screen_allow_remote_input"; /** - * Indicates which clock face to show on lock screen and AOD. + * Indicates which clock face to show on lock screen and AOD formatted as a serialized + * {@link org.json.JSONObject} with the format: + * {"clock": id, "_applied_timestamp": timestamp} * @hide */ public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face"; private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR = - ANY_STRING_VALIDATOR; + SettingsValidators.JSON_OBJECT_VALIDATOR; /** * Indicates which clock face to show on lock screen and AOD while docked. @@ -7731,12 +7734,21 @@ public final class Settings { private static final Validator DOZE_TAP_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** - * Gesture that wakes up the display, showing the ambient version of the status bar. + * Gesture that wakes up the display, showing some version of the lock screen. + * @hide + */ + public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_screen_gesture"; + + private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** + * Gesture that wakes up the display, toggling between {@link Display.STATE_OFF} and + * {@link Display.STATE_DOZE}. * @hide */ - public static final String DOZE_WAKE_SCREEN_GESTURE = "doze_wake_screen_gesture"; + public static final String DOZE_WAKE_DISPLAY_GESTURE = "doze_wake_display_gesture"; - private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + private static final Validator DOZE_WAKE_DISPLAY_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Gesture that skips media. @@ -7752,10 +7764,30 @@ public final class Settings { */ public static final String SKIP_GESTURE_COUNT = "skip_gesture_count"; + /** + * Count of non-gesture interaction. + * @hide + */ + public static final String SKIP_TOUCH_COUNT = "skip_touch_count"; + private static final Validator SKIP_GESTURE_COUNT_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; /** + * Direction to advance media for skip gesture + * @hide + */ + public static final String SKIP_DIRECTION = "skip_gesture_direction"; + + /** + * Only used if FeatureFlag "settings_skip_direction_mutable" is enabled. + * If feature flag is disabled, should assume SKIP_DIRECTION = 0. + * 0 / false = right to left to advance to next + * 1 / true = left to right to advance to next + */ + private static final Validator SKIP_DIRECTION_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * Gesture that silences sound (alarms, notification, calls). * @hide */ @@ -7782,11 +7814,22 @@ public final class Settings { public static final String SILENCE_CALL_GESTURE_COUNT = "silence_call_gesture_count"; /** - * Count of successful silence notification gestures. + * Count of non-gesture interaction. * @hide */ - public static final String SILENCE_NOTIFICATION_GESTURE_COUNT = - "silence_notification_gesture_count"; + public static final String SILENCE_ALARMS_TOUCH_COUNT = "silence_alarms_touch_count"; + + /** + * Count of non-gesture interaction. + * @hide + */ + public static final String SILENCE_TIMER_TOUCH_COUNT = "silence_timer_touch_count"; + + /** + * Count of non-gesture interaction. + * @hide + */ + public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count"; private static final Validator SILENCE_GESTURE_COUNT_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; @@ -8250,6 +8293,16 @@ public final class Settings { BOOLEAN_VALIDATOR; /** + * Whether or not media is shown automatically when bypassing as a heads up. + * @hide + */ + public static final String SHOW_MEDIA_WHEN_BYPASSING = + "show_media_when_bypassing"; + + private static final Validator SHOW_MEDIA_WHEN_BYPASSING_VALIDATOR = + BOOLEAN_VALIDATOR; + + /** * Whether or not face unlock requires attention. This is a cached value, the source of * truth is obtained through the HAL. * @hide @@ -8289,16 +8342,6 @@ public final class Settings { BOOLEAN_VALIDATOR; /** - * Whether or not the face unlock education screen has been shown to the user. - * @hide - */ - public static final String FACE_UNLOCK_EDUCATION_INFO_DISPLAYED = - "face_unlock_education_info_displayed"; - - private static final Validator FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR = - BOOLEAN_VALIDATOR; - - /** * Whether or not debugging is enabled. * @hide */ @@ -8948,10 +8991,12 @@ public final class Settings { DOZE_PICK_UP_GESTURE, DOZE_DOUBLE_TAP_GESTURE, DOZE_TAP_SCREEN_GESTURE, - DOZE_WAKE_SCREEN_GESTURE, + DOZE_WAKE_LOCK_SCREEN_GESTURE, + DOZE_WAKE_DISPLAY_GESTURE, NFC_PAYMENT_DEFAULT_COMPONENT, AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, FACE_UNLOCK_KEYGUARD_ENABLED, + SHOW_MEDIA_WHEN_BYPASSING, FACE_UNLOCK_DISMISSES_KEYGUARD, FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, @@ -8992,15 +9037,19 @@ public final class Settings { UI_NIGHT_MODE, LOCK_SCREEN_WHEN_TRUST_LOST, SKIP_GESTURE, + SKIP_DIRECTION, SILENCE_GESTURE, THEME_CUSTOMIZATION_OVERLAY_PACKAGES, NAVIGATION_MODE, AWARE_ENABLED, SKIP_GESTURE_COUNT, + SKIP_TOUCH_COUNT, SILENCE_ALARMS_GESTURE_COUNT, - SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_CALL_GESTURE_COUNT, SILENCE_TIMER_GESTURE_COUNT, + SILENCE_ALARMS_TOUCH_COUNT, + SILENCE_CALL_TOUCH_COUNT, + SILENCE_TIMER_TOUCH_COUNT, DARK_MODE_DIALOG_SEEN, GLOBAL_ACTIONS_PANEL_ENABLED, AWARE_LOCK_ENABLED @@ -9119,18 +9168,18 @@ public final class Settings { VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_TAP_SCREEN_GESTURE, DOZE_TAP_SCREEN_GESTURE_VALIDATOR); - VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR); + VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR); + VALIDATORS.put(DOZE_WAKE_DISPLAY_GESTURE, DOZE_WAKE_DISPLAY_GESTURE_VALIDATOR); VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR); VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_DISMISSES_KEYGUARD, FACE_UNLOCK_DISMISSES_KEYGUARD_VALIDATOR); + VALIDATORS.put(SHOW_MEDIA_WHEN_BYPASSING, SHOW_MEDIA_WHEN_BYPASSING_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR); - VALIDATORS.put(FACE_UNLOCK_EDUCATION_INFO_DISPLAYED, - FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR); VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR); VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR); @@ -9178,16 +9227,21 @@ public final class Settings { VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR); VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR); VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR); + VALIDATORS.put(SKIP_DIRECTION, SKIP_DIRECTION_VALIDATOR); + VALIDATORS.put(SKIP_DIRECTION, SKIP_DIRECTION_VALIDATOR); VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR); VALIDATORS.put(THEME_CUSTOMIZATION_OVERLAY_PACKAGES, THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR); VALIDATORS.put(NAVIGATION_MODE, NAVIGATION_MODE_VALIDATOR); VALIDATORS.put(AWARE_ENABLED, AWARE_ENABLED_VALIDATOR); VALIDATORS.put(SKIP_GESTURE_COUNT, SKIP_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SKIP_TOUCH_COUNT, SKIP_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_ALARMS_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_TIMER_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); - VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_ALARMS_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_TIMER_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_CALL_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR); VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR); VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR); @@ -13549,6 +13603,16 @@ public final class Settings { "location_settings_link_to_permissions_enabled"; /** + * Flag to set the waiting time for removing invisible euicc profiles inside System > + * Settings. + * Type: long + * + * @hide + */ + public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = + "euicc_removing_invisible_profiles_timeout_millis"; + + /** * Flag to set the waiting time for euicc factory reset inside System > Settings * Type: long * diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 324e02ceb1d5..0e0c676c2782 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -57,6 +57,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false"); DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); + DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true"); } /** diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index bbd44c8b85af..4b929683fd6d 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -830,6 +830,32 @@ public final class AccessibilityInteractionController { return false; } + private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) { + if (infos == null || shouldBypassAdjustBoundsInScreen()) { + return; + } + final int infoCount = infos.size(); + for (int i = 0; i < infoCount; i++) { + final AccessibilityNodeInfo info = infos.get(i); + adjustBoundsInScreenIfNeeded(info); + } + } + + private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) { + if (info == null || shouldBypassAdjustBoundsInScreen()) { + return; + } + final Rect boundsInScreen = mTempRect; + info.getBoundsInScreen(boundsInScreen); + boundsInScreen.offset(mViewRootImpl.mAttachInfo.mLocationInParentDisplay.x, + mViewRootImpl.mAttachInfo.mLocationInParentDisplay.y); + info.setBoundsInScreen(boundsInScreen); + } + + private boolean shouldBypassAdjustBoundsInScreen() { + return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0); + } + private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, MagnificationSpec spec) { if (info == null) { @@ -921,6 +947,7 @@ public final class AccessibilityInteractionController { MagnificationSpec spec, Region interactiveRegion) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + adjustBoundsInScreenIfNeeded(infos); applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); @@ -939,6 +966,7 @@ public final class AccessibilityInteractionController { MagnificationSpec spec, Region interactiveRegion) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + adjustBoundsInScreenIfNeeded(info); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); adjustIsVisibleToUserIfNeeded(info, interactiveRegion); callback.setFindAccessibilityNodeInfoResult(info, interactionId); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index c64386e8db79..e95b5caa4fa0 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.DisplayEventReceiver.CONFIG_CHANGED_EVENT_SUPPRESS; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP; import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER; @@ -910,7 +911,7 @@ public final class Choreographer { private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { - super(looper, vsyncSource); + super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index 60daddd663d5..91acc4638c26 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -53,6 +53,20 @@ public abstract class DisplayEventReceiver { */ public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; + /** + * Specifies to suppress config changed events from being generated from Surface Flinger. + * <p> + * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h + */ + public static final int CONFIG_CHANGED_EVENT_SUPPRESS = 0; + + /** + * Specifies to generate config changed events from Surface Flinger. + * <p> + * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h + */ + public static final int CONFIG_CHANGED_EVENT_DISPATCH = 1; + private static final String TAG = "DisplayEventReceiver"; private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -65,7 +79,7 @@ public abstract class DisplayEventReceiver { private MessageQueue mMessageQueue; private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, - MessageQueue messageQueue, int vsyncSource); + MessageQueue messageQueue, int vsyncSource, int configChanged); private static native void nativeDispose(long receiverPtr); @FastNative private static native void nativeScheduleVsync(long receiverPtr); @@ -77,7 +91,7 @@ public abstract class DisplayEventReceiver { */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { - this(looper, VSYNC_SOURCE_APP); + this(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_SUPPRESS); } /** @@ -85,15 +99,17 @@ public abstract class DisplayEventReceiver { * * @param looper The looper to use when invoking callbacks. * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values. + * @param configChanged Whether to dispatch config changed events. Must be one of the + * CONFIG_CHANGED_EVENT_* values. */ - public DisplayEventReceiver(Looper looper, int vsyncSource) { + public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, - vsyncSource); + vsyncSource, configChanged); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 956161acd762..955be8d40c47 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -77,6 +77,21 @@ interface IRecentsAnimationController { void hideCurrentInputMethod(); /** + * This call is deprecated, use #setDeferCancelUntilNextTransition() instead + * TODO(138144750): Remove this method once there are no callers + * @deprecated + */ + void setCancelWithDeferredScreenshot(boolean screenshot); + + /** + * Clean up the screenshot of previous task which was created during recents animation that + * was cancelled by a stack order change. + * + * @see {@link IRecentsAnimationRunner#onAnimationCanceled} + */ + void cleanupScreenshot(); + + /** * Set a state for controller whether would like to cancel recents animations with deferred * task screenshot presentation. * @@ -86,22 +101,23 @@ interface IRecentsAnimationController { * screenshot, so that Launcher can still control the leash lifecycle & make the next app * transition animate smoothly without flickering. * - * @param screenshot When set {@code true}, means recents animation will be canceled when the - * next app launch. System will take previous task's screenshot when the next - * app transition starting, and skip previous task's animation. - * Set {@code false} means will not take screenshot & skip animation - * for previous task. + * @param defer When set {@code true}, means that the recents animation will defer canceling the + * animation when a stack order change is triggered until the subsequent app + * transition start and skip previous task's animation. + * When set to {@code false}, means that the recents animation will be canceled + * immediately when the stack order changes. + * @param screenshot When set {@code true}, means that the system will take previous task's + * screenshot and replace the contents of the leash with it when the next app + * transition starting. The runner must call #cleanupScreenshot() to end the + * recents animation. + * When set to {@code false}, means that the system will simply wait for the + * next app transition start to immediately cancel the recents animation. This + * can be useful when you want an immediate transition into a state where the + * task is shown in the home/recents activity (without waiting for a + * screenshot). * * @see #cleanupScreenshot() * @see IRecentsAnimationRunner#onCancelled */ - void setCancelWithDeferredScreenshot(boolean screenshot); - - /** - * Clean up the screenshot of previous task which was created during recents animation that - * was cancelled by a stack order change. - * - * @see {@link IRecentsAnimationRunner#onAnimationCanceled} - */ - void cleanupScreenshot(); + void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot); } diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index 9c652a8d990b..6cda60c80b7d 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -36,7 +36,7 @@ oneway interface IRecentsAnimationRunner { * @param deferredWithScreenshot If set to {@code true}, the contents of the task will be * replaced with a screenshot, such that the runner's leash is * still active. As soon as the runner doesn't need the leash - * anymore, it can call + * anymore, it must call * {@link IRecentsAnimationController#cleanupScreenshot). * * @see {@link RecentsAnimationController#cleanupScreenshot} diff --git a/core/java/android/view/ISystemGestureExclusionListener.aidl b/core/java/android/view/ISystemGestureExclusionListener.aidl index a032625547d2..9c2f9a6a192c 100644 --- a/core/java/android/view/ISystemGestureExclusionListener.aidl +++ b/core/java/android/view/ISystemGestureExclusionListener.aidl @@ -28,7 +28,14 @@ oneway interface ISystemGestureExclusionListener { * Called when the system gesture exclusion for the given display changed. * @param displayId the display whose system gesture exclusion changed * @param systemGestureExclusion a {@code Region} where the app would like priority over the - * system gestures, in display coordinates. + * system gestures, in display coordinates. Certain restrictions + * might be applied such that apps don't get all the exclusions + * they request. + * @param systemGestureExclusionUnrestricted a {@code Region} where the app would like priority + * over the system gestures, in display coordinates, without + * any restrictions applied. Null if no restrictions have been + * applied. */ - void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion); + void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion, + in Region systemGestureExclusionUnrestricted); }
\ No newline at end of file diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 699e795be980..f34f9e6d5ce8 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -17,6 +17,7 @@ package android.view; +import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; import android.os.ParcelFileDescriptor; @@ -57,6 +58,12 @@ oneway interface IWindow { in DisplayCutout.ParcelableWrapper displayCutout); /** + * Called when the window location in parent display has changed. The offset will only be a + * nonzero value if the window is on an embedded display that is re-parented to another window. + */ + void locationInParentDisplayChanged(in Point offset); + + /** * Called when the window insets configuration has changed. */ void insetsChanged(in InsetsState insetsState); diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java index bc2fe545a8ac..c686440171a2 100644 --- a/core/java/android/view/RemoteAnimationAdapter.java +++ b/core/java/android/view/RemoteAnimationAdapter.java @@ -55,6 +55,7 @@ public class RemoteAnimationAdapter implements Parcelable { /** @see #getCallingPid */ private int mCallingPid; + private int mCallingUid; /** * @param runner The interface that gets notified when we actually need to start the animation. @@ -103,10 +104,11 @@ public class RemoteAnimationAdapter implements Parcelable { } /** - * To be called by system_server to keep track which pid is running this animation. + * To be called by system_server to keep track which pid and uid is running this animation. */ - public void setCallingPid(int pid) { + public void setCallingPidUid(int pid, int uid) { mCallingPid = pid; + mCallingUid = uid; } /** @@ -116,6 +118,13 @@ public class RemoteAnimationAdapter implements Parcelable { return mCallingPid; } + /** + * @return The uid of the process running the animation. + */ + public int getCallingUid() { + return mCallingUid; + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java index 884cae440bed..da599efb6eee 100644 --- a/core/java/android/view/RemoteAnimationDefinition.java +++ b/core/java/android/view/RemoteAnimationDefinition.java @@ -118,9 +118,9 @@ public class RemoteAnimationDefinition implements Parcelable { * To be called by system_server to keep track which pid is running the remote animations inside * this definition. */ - public void setCallingPid(int pid) { + public void setCallingPidUid(int pid, int uid) { for (int i = mTransitionAnimationMap.size() - 1; i >= 0; i--) { - mTransitionAnimationMap.valueAt(i).adapter.setCallingPid(pid); + mTransitionAnimationMap.valueAt(i).adapter.setCallingPidUid(pid, uid); } } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index cb64ab1fd921..17f07b5a2ad4 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.content.res.CompatibilityInfo.Translator; import android.graphics.Canvas; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.RecordingCanvas; @@ -80,7 +81,8 @@ public class Surface implements Parcelable { private static native long nativeGetNextFrameNumber(long nativeObject); private static native int nativeSetScalingMode(long nativeObject, int scalingMode); private static native int nativeForceScopedDisconnect(long nativeObject); - private static native int nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer); + private static native int nativeAttachAndQueueBufferWithColorSpace(long nativeObject, + GraphicBuffer buffer, int colorSpaceId); private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled); private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled); @@ -699,21 +701,38 @@ public class Surface implements Parcelable { } /** - * Transfer ownership of buffer and present it on the Surface. + * Transfer ownership of buffer with a color space and present it on the Surface. + * The supported color spaces are SRGB and Display P3, other color spaces will be + * treated as SRGB. * @hide */ - public void attachAndQueueBuffer(GraphicBuffer buffer) { + public void attachAndQueueBufferWithColorSpace(GraphicBuffer buffer, ColorSpace colorSpace) { synchronized (mLock) { checkNotReleasedLocked(); - int err = nativeAttachAndQueueBuffer(mNativeObject, buffer); + if (colorSpace == null) { + colorSpace = ColorSpace.get(ColorSpace.Named.SRGB); + } + int err = nativeAttachAndQueueBufferWithColorSpace(mNativeObject, buffer, + colorSpace.getId()); if (err != 0) { throw new RuntimeException( - "Failed to attach and queue buffer to Surface (bad object?)"); + "Failed to attach and queue buffer to Surface (bad object?), " + + "native error: " + err); } } } /** + * Deprecated, use attachAndQueueBufferWithColorSpace instead. + * Transfer ownership of buffer and present it on the Surface. + * The color space of the buffer is treated as SRGB. + * @hide + */ + public void attachAndQueueBuffer(GraphicBuffer buffer) { + attachAndQueueBufferWithColorSpace(buffer, ColorSpace.get(ColorSpace.Named.SRGB)); + } + + /** * Returns whether or not this Surface is backed by a single-buffered SurfaceTexture * @hide */ diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 63e14853b51d..3c045f4fdb88 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1866,7 +1866,8 @@ public final class SurfaceControl implements Parcelable { final ScreenshotGraphicBuffer buffer = screenshotToBuffer(display, sourceCrop, width, height, useIdentityTransform, rotation); try { - consumer.attachAndQueueBuffer(buffer.getGraphicBuffer()); + consumer.attachAndQueueBufferWithColorSpace(buffer.getGraphicBuffer(), + buffer.getColorSpace()); } catch (RuntimeException e) { Log.w(TAG, "Failed to take screenshot - " + e.getMessage()); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1275e46ed421..4e86e60fdd62 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1390,6 +1390,74 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1; + /** @hide */ + @IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = { + IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, + IMPORTANT_FOR_CONTENT_CAPTURE_YES, + IMPORTANT_FOR_CONTENT_CAPTURE_NO, + IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, + IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ContentCaptureImportance {} + + /** + * Automatically determine whether a view is important for content capture. + * + * @see #isImportantForContentCapture() + * @see #setImportantForContentCapture(int) + * + * @hide + */ + @TestApi + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0; + + /** + * The view is important for content capture, and its children (if any) will be traversed. + * + * @see #isImportantForContentCapture() + * @see #setImportantForContentCapture(int) + * + * @hide + */ + @TestApi + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1; + + /** + * The view is not important for content capture, but its children (if any) will be traversed. + * + * @see #isImportantForContentCapture() + * @see #setImportantForContentCapture(int) + * + * @hide + */ + @TestApi + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2; + + /** + * The view is important for content capture, but its children (if any) will not be traversed. + * + * @see #isImportantForContentCapture() + * @see #setImportantForContentCapture(int) + * + * @hide + */ + @TestApi + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4; + + /** + * The view is not important for content capture, and its children (if any) will not be + * traversed. + * + * @see #isImportantForContentCapture() + * @see #setImportantForContentCapture(int) + * + * @hide + */ + @TestApi + public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8; + + /** * This view is enabled. Interpretation varies by subclass. * Use with ENABLED_MASK when calling setFlags. @@ -3362,6 +3430,55 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /* End of masks for mPrivateFlags3 */ + /* + * Masks for mPrivateFlags4, as generated by dumpFlags(): + * + * |-------|-------|-------|-------| + * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK + * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED + * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED + * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED + * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE + * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK + * |-------|-------|-------|-------| + */ + + /** + * Mask for obtaining the bits which specify how to determine + * whether a view is important for autofill. + * + * <p>NOTE: the important for content capture values were the first flags added and are set in + * the rightmost position, so we don't need to shift them + */ + private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK = + IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES + | IMPORTANT_FOR_CONTENT_CAPTURE_NO + | IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS + | IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS; + + /* + * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods + * should be called. + * + * The idea is to call notifyAppeared() after the view is layout and visible, then call + * notifyDisappeared() when it's gone (without known when it was removed from the parent). + */ + private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10; + private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20; + + /* + * Flags used to cache the value returned by isImportantForContentCapture while the view + * hierarchy is being traversed. + */ + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40; + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80; + + private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK = + PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED + | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + + /* End of masks for mPrivateFlags4 */ + /** @hide */ protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0; /** @hide */ @@ -3985,6 +4102,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060) int mPrivateFlags3; + private int mPrivateFlags4; + /** * This view's request for the visibility of the status bar. * @hide @@ -8442,6 +8561,65 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onProvideStructure(structure, VIEW_STRUCTURE_FOR_AUTOFILL, flags); } + /** + * Populates a {@link ViewStructure} for content capture. + * + * <p>This method is called after a view is that is eligible for content capture + * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for + * the user, and the activity rendering the view is enabled for content capture) is laid out and + * is visible. + * + * <p>The populated structure is then passed to the service through + * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}. + * + * <p><b>Note: </b>views that manage a virtual structure under this view must populate just + * the node representing this view and return right away, then asynchronously report (not + * necessarily in the UI thread) when the children nodes appear, disappear or have their text + * changed by calling + * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, + * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and + * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} + * respectively. The structure for the a child must be created using + * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the + * {@code autofillId} for a child can be obtained either through + * {@code childStructure.getAutofillId()} or + * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}. + * + * <p>When the virtual view hierarchy represents a web page, you should also: + * + * <ul> + * <li>Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content + * capture events should be generate for that URL. + * <li>Create a new {@link ContentCaptureSession} child for every HTML element that + * renders a new URL (like an {@code IFRAME}) and use that session to notify events from + * that subtree. + * </ul> + * + * <p><b>Note: </b>the following methods of the {@code structure} will be ignored: + * <ul> + * <li>{@link ViewStructure#setChildCount(int)} + * <li>{@link ViewStructure#addChildCount(int)} + * <li>{@link ViewStructure#getChildCount()} + * <li>{@link ViewStructure#newChild(int)} + * <li>{@link ViewStructure#asyncNewChild(int)} + * <li>{@link ViewStructure#asyncCommit()} + * <li>{@link ViewStructure#setWebDomain(String)} + * <li>{@link ViewStructure#newHtmlInfoBuilder(String)} + * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)} + * <li>{@link ViewStructure#setDataIsSensitive(boolean)} + * <li>{@link ViewStructure#setAlpha(float)} + * <li>{@link ViewStructure#setElevation(float)} + * <li>{@link ViewStructure#setTransformation(Matrix)} + * + * </ul> + * + * @hide + */ + @TestApi + public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) { + onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags); + } + /** @hide */ protected void onProvideStructure(@NonNull ViewStructure structure, @ViewStructureType int viewFor, int flags) { @@ -9080,6 +9258,280 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Gets the mode for determining whether this view is important for content capture. + * + * <p>See {@link #setImportantForContentCapture(int)} and + * {@link #isImportantForContentCapture()} for more info about this mode. + * + * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to + * {@link #setImportantForContentCapture(int)}. + * + * @attr ref android.R.styleable#View_importantForContentCapture + * + * @hide + */ + @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"), + @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"), + @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"), + @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, + to = "yesExcludeDescendants"), + @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, + to = "noExcludeDescendants")}) +// @InspectableProperty(enumMapping = { +// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, name = "auto"), +// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES, name = "yes"), +// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO, name = "no"), +// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, +// name = "yesExcludeDescendants"), +// @EnumEntry(value = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, +// name = "noExcludeDescendants"), +// }) + @TestApi + public @ContentCaptureImportance int getImportantForContentCapture() { + // NOTE: the important for content capture values were the first flags added and are set in + // the rightmost position, so we don't need to shift them + return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; + } + + /** + * Sets the mode for determining whether this view is considered important for content capture. + * + * <p>The platform determines the importance for autofill automatically but you + * can use this method to customize the behavior. Typically, a view that provides text should + * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}. + * + * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}, + * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO}, + * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS}, + * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}. + * + * @attr ref android.R.styleable#View_importantForContentCapture + * + * @hide + */ + @TestApi + public void setImportantForContentCapture(@ContentCaptureImportance int mode) { + // Reset first + mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK; + // Then set again + // NOTE: the important for content capture values were the first flags added and are set in + // the rightmost position, so we don't need to shift them + mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK); + } + + /** + * Hints the Android System whether this view is considered important for content capture, based + * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics + * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. + * + * <p>See {@link ContentCaptureManager} for more info about content capture. + * + * @return whether the view is considered important for content capture. + * + * @see #setImportantForContentCapture(int) + * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO + * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES + * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO + * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS + * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS + * + * @hide + */ + @TestApi + public final boolean isImportantForContentCapture() { + boolean isImportant; + if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) { + isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0; + return isImportant; + } + + isImportant = calculateIsImportantForContentCapture(); + + mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + if (isImportant) { + mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE; + } + mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED; + return isImportant; + } + + /** + * Calculates whether the flag is important for content capture so it can be used by + * {@link #isImportantForContentCapture()} while the tree is traversed. + */ + private boolean calculateIsImportantForContentCapture() { + // Check parent mode to ensure we're important + ViewParent parent = mParent; + while (parent instanceof View) { + final int parentImportance = ((View) parent).getImportantForContentCapture(); + if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS + || parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for " + + "content capture because parent " + parent + "'s importance is " + + parentImportance); + } + return false; + } + parent = parent.getParent(); + } + + final int importance = getImportantForContentCapture(); + + // First, check the explicit states. + if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS + || importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) { + return true; + } + if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS + || importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for content " + + "capture because its importance is " + importance); + } + return false; + } + + // Then use some heuristics to handle AUTO. + if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance + + " on view " + this); + return false; + } + + // View group is important if at least one children also is + if (this instanceof ViewGroup) { + final ViewGroup group = (ViewGroup) this; + for (int i = 0; i < group.getChildCount(); i++) { + final View child = group.getChildAt(i); + if (child.isImportantForContentCapture()) { + return true; + } + } + } + + // If the app developer explicitly set hints or autofill hintsfor it, it's important. + if (getAutofillHints() != null) { + return true; + } + + // Otherwise, assume it's not important... + return false; + } + + /** + * Helper used to notify the {@link ContentCaptureManager} when the view is removed or + * added, based on whether it's laid out and visible, and without knowing if the parent removed + * it from the view hierarchy. + * + * <p>This method is called from many places (visibility changed, view laid out, view attached + * or detached to/from window, etc...) and hence must contain the logic to call the manager, as + * described below: + * + * <ol> + * <li>It should only be called when content capture is enabled for the view. + * <li>It must call viewAppeared() before viewDisappeared() + * <li>viewAppearead() can only be called when the view is visible and laidout + * <li>It should not call the same event twice. + * </ol> + */ + private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) { + AttachInfo ai = mAttachInfo; + // Skip it while the view is being laided out for the first time + if (ai != null && !ai.mReadyForContentCaptureUpdates) return; + + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, + "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName()); + } + try { + notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(appeared); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + + private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) { + AttachInfo ai = mAttachInfo; + + // First check if context has client, so it saves a service lookup when it doesn't + if (mContext.getContentCaptureOptions() == null) return; + + // Then check if it's enabled in the context... + final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext) + : mContext.getSystemService(ContentCaptureManager.class); + if (ccm == null || !ccm.isContentCaptureEnabled()) return; + + // ... and finally at the view level + // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled() + if (!isImportantForContentCapture()) return; + + ContentCaptureSession session = getContentCaptureSession(); + if (session == null) return; + + if (appeared) { + if (!isLaidOut() || getVisibility() != VISIBLE + || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid=" + + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + + ", visible=" + (getVisibility() == VISIBLE) + + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + } + return; + } + setNotifiedContentCaptureAppeared(); + + if (ai != null) { + ai.delayNotifyContentCaptureEvent(session, this, appeared); + } else { + if (DEBUG_CONTENT_CAPTURE) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this); + } + } + } else { + if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 + || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid=" + + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + + ", visible=" + (getVisibility() == VISIBLE) + + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) + + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 + & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + } + return; + } + mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; + mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; + + if (ai != null) { + ai.delayNotifyContentCaptureEvent(session, this, appeared); + } else { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this); + } + } + } + } + + private void setNotifiedContentCaptureAppeared() { + mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; + mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; + } + + /** @hide */ + protected boolean getNotifiedContentCaptureAppeared() { + return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0; + } + + + /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to @@ -9332,6 +9784,68 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Dispatches the initial content capture events for a view structure. + * + * @hide + */ + public void dispatchInitialProvideContentCaptureStructure() { + AttachInfo ai = mAttachInfo; + if (ai == null) { + Log.w(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this); + return; + } + ContentCaptureManager ccm = ai.mContentCaptureManager; + if (ccm == null) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): " + + "no ContentCaptureManager for " + this); + return; + } + + // We must set it before checkign if the view itself is important, because it might + // initially not be (for example, if it's empty), although that might change later (for + // example, if important views are added) + ai.mReadyForContentCaptureUpdates = true; + + if (!isImportantForContentCapture()) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { + Log.d(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): decorView is not important"); + } + return; + } + + ai.mContentCaptureManager = ccm; + + ContentCaptureSession session = getContentCaptureSession(); + if (session == null) { + if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) { + Log.d(CONTENT_CAPTURE_LOG_TAG, + "dispatchProvideContentCaptureStructure(): no session for " + this); + } + return; + } + + session.internalNotifyViewTreeEvent(/* started= */ true); + try { + dispatchProvideContentCaptureStructure(); + } finally { + session.internalNotifyViewTreeEvent(/* started= */ false); + } + } + + /** @hide */ + void dispatchProvideContentCaptureStructure() { + ContentCaptureSession session = getContentCaptureSession(); + if (session != null) { + ViewStructure structure = session.newViewStructure(this); + onProvideContentCaptureStructure(structure, /* flags= */ 0); + setNotifiedContentCaptureAppeared(); + session.notifyViewAppeared(structure); + } + } + + /** * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * * Note: Called from the default {@link AccessibilityDelegate}. @@ -10571,6 +11085,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * <p>Do not modify the provided list after this method is called.</p> * + * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the + * exclusions it takes into account. The limit does not apply while the navigation + * bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the + * {@link android.inputmethodservice.InputMethodService input method} and + * {@link Intent#CATEGORY_HOME home activity}. + * </p> + * * @param rects A list of precision gesture regions that this view needs to function correctly */ public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { @@ -13281,6 +13802,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void dispatchStartTemporaryDetach() { mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH; notifyEnterOrExitForAutoFillIfNeeded(false); + notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); onStartTemporaryDetach(); } @@ -13307,6 +13829,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyFocusChangeToInputMethodManager(true /* hasFocus */); } notifyEnterOrExitForAutoFillIfNeeded(true); + notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } /** @@ -13898,6 +14421,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); } } + + notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible); } /** @@ -17593,6 +18118,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // Reset content capture caches + mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; mCachedContentCaptureSession = null; if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) @@ -19602,6 +20128,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, needGlobalAttributesUpdate(false); notifyEnterOrExitForAutoFillIfNeeded(true); + notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -19651,6 +20178,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } notifyEnterOrExitForAutoFillIfNeeded(false); + notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); } /** @@ -21985,6 +22513,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; notifyEnterOrExitForAutoFillIfNeeded(true); } + + notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); } private boolean hasParentWantsFocus() { @@ -28050,6 +28580,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHandlingPointerEvent; /** + * The offset of this view's window when it's on an embedded display that is re-parented + * to another window. + */ + final Point mLocationInParentDisplay = new Point(); + + /** * Global to the view hierarchy used as a temporary for dealing with * x/y points in the transparent region computations. */ @@ -28196,6 +28732,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, View mTooltipHost; /** + * The initial structure has been reported so the view is ready to report updates. + */ + boolean mReadyForContentCaptureUpdates; + + /** + * Map(keyed by session) of content capture events that need to be notified after the view + * hierarchy is traversed: value is either the view itself for appearead events, or its + * autofill id for disappeared. + */ + SparseArray<ArrayList<Object>> mContentCaptureEvents; + + /** + * Cached reference to the {@link ContentCaptureManager}. + */ + ContentCaptureManager mContentCaptureManager; + + /** * Creates a new set of attachment information with the specified * events handler and thread. * @@ -28213,6 +28766,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRootCallbacks = effectPlayer; mTreeObserver = new ViewTreeObserver(context); } + + private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session, + @NonNull View view, boolean appeared) { + if (mContentCaptureEvents == null) { + // Most of the time there will be just one session, so intial capacity is 1 + mContentCaptureEvents = new SparseArray<>(1); + } + int sessionId = session.getId(); + // TODO: life would be much easier if we provided a MultiMap implementation somwhere... + ArrayList<Object> events = mContentCaptureEvents.get(sessionId); + if (events == null) { + events = new ArrayList<>(); + mContentCaptureEvents.put(sessionId, events); + } + events.add(appeared ? view : view.getAutofillId()); + } + + @Nullable + ContentCaptureManager getContentCaptureManager(@NonNull Context context) { + if (mContentCaptureManager != null) { + return mContentCaptureManager; + } + mContentCaptureManager = context.getSystemService(ContentCaptureManager.class); + return mContentCaptureManager; + } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d362024ed525..937bd1b34e61 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3606,7 +3606,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return; } - final ChildListForAutofill children = getChildrenForAutofill(flags); + final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags); final int childrenCount = children.size(); structure.setChildCount(childrenCount); for (int i = 0; i < childrenCount; i++) { @@ -3617,14 +3617,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager children.recycle(); } + /** @hide */ + @Override + public void dispatchProvideContentCaptureStructure() { + super.dispatchProvideContentCaptureStructure(); + + if (!isLaidOut()) return; + + final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture(); + final int childrenCount = children.size(); + for (int i = 0; i < childrenCount; i++) { + final View child = children.get(i); + child.dispatchProvideContentCaptureStructure(); + } + children.recycle(); + } + /** * Gets the children for autofill. Children for autofill are the first * level descendants that are important for autofill. The returned * child list object is pooled and the caller must recycle it once done. * @hide */ - private @NonNull ChildListForAutofill getChildrenForAutofill( + private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill( @AutofillFlags int flags) { - final ChildListForAutofill children = ChildListForAutofill + final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture .obtain(); populateChildrenForAutofill(children, flags); return children; @@ -3652,6 +3668,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() { + final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture + .obtain(); + populateChildrenForContentCapture(children); + return children; + } + + /** @hide */ + private void populateChildrenForContentCapture(ArrayList<View> list) { + final int childrenCount = mChildrenCount; + if (childrenCount <= 0) { + return; + } + final ArrayList<View> preorderedList = buildOrderedChildList(); + final boolean customOrder = preorderedList == null + && isChildrenDrawingOrderEnabled(); + for (int i = 0; i < childrenCount; i++) { + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + final View child = (preorderedList == null) + ? mChildren[childIndex] : preorderedList.get(childIndex); + if (child.isImportantForContentCapture()) { + list.add(child); + } else if (child instanceof ViewGroup) { + ((ViewGroup) child).populateChildrenForContentCapture(list); + } + } + } + private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, int childIndex) { final View child; @@ -8634,16 +8678,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Pooled class that to hold the children for autifill. */ - private static class ChildListForAutofill extends ArrayList<View> { + private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> { private static final int MAX_POOL_SIZE = 32; - private static final Pools.SimplePool<ChildListForAutofill> sPool = + private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool = new Pools.SimplePool<>(MAX_POOL_SIZE); - public static ChildListForAutofill obtain() { - ChildListForAutofill list = sPool.acquire(); + public static ChildListForAutoFillOrContentCapture obtain() { + ChildListForAutoFillOrContentCapture list = sPool.acquire(); if (list == null) { - list = new ChildListForAutofill(); + list = new ChildListForAutoFillOrContentCapture(); } return list; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index aceb276e9fc0..440df89f814f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -105,7 +105,11 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; +import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -220,6 +224,21 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; + /** + * Initial value for {@link #mContentCaptureEnabled}. + */ + private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}. + */ + private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}. + */ + private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; + @UnsupportedAppUsage static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -410,6 +429,10 @@ public final class ViewRootImpl implements ViewParent, boolean mLayoutRequested; boolean mFirst; + @Nullable + int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; + boolean mPerformContentCapture; + boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -607,6 +630,7 @@ public final class ViewRootImpl implements ViewParent, mTransparentRegion = new Region(); mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added + mPerformContentCapture = true; // also true for the first time the view is added mAdded = false; mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); @@ -2765,9 +2789,55 @@ public final class ViewRootImpl implements ViewParent, } } + if (mAttachInfo.mContentCaptureEvents != null) { + notifyContentCatpureEvents(); + } + mIsInTraversal = false; } + private void notifyContentCatpureEvents() { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); + try { + MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager + .getMainContentCaptureSession(); + for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { + int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i); + mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); + ArrayList<Object> events = mAttachInfo.mContentCaptureEvents + .valueAt(i); + for_each_event: for (int j = 0; j < events.size(); j++) { + Object event = events.get(j); + if (event instanceof AutofillId) { + mainSession.notifyViewDisappeared(sessionId, (AutofillId) event); + } else if (event instanceof View) { + View view = (View) event; + ContentCaptureSession session = view.getContentCaptureSession(); + if (session == null) { + Log.w(mTag, "no content capture session on view: " + view); + continue for_each_event; + } + int actualId = session.getId(); + if (actualId != sessionId) { + Log.w(mTag, "content capture session mismatch for view (" + view + + "): was " + sessionId + " before, it's " + actualId + " now"); + continue for_each_event; + } + ViewStructure structure = session.newViewStructure(view); + view.onProvideContentCaptureStructure(structure, /* flags= */ 0); + session.notifyViewAppeared(structure); + } else { + Log.w(mTag, "invalid content capture event: " + event); + } + } + mainSession.notifyViewTreeEvent(sessionId, /* started= */ false); + } + mAttachInfo.mContentCaptureEvents = null; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + private void notifySurfaceDestroyed() { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); @@ -2902,6 +2972,13 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + + // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus + // is lost, so we don't need to to force a flush - there might be other events such as + // text changes, but these should be flushed independently. + if (hasWindowFocus) { + handleContentCaptureFlush(); + } } private void fireAccessibilityFocusEventIfHasFocusedNode() { @@ -3468,6 +3545,86 @@ public final class ViewRootImpl implements ViewParent, pendingDrawFinished(); } } + if (mPerformContentCapture) { + performContentCaptureInitialReport(); + } + } + + /** + * Checks (and caches) if content capture is enabled for this context. + */ + private boolean isContentCaptureEnabled() { + switch (mContentCaptureEnabled) { + case CONTENT_CAPTURE_ENABLED_TRUE: + return true; + case CONTENT_CAPTURE_ENABLED_FALSE: + return false; + case CONTENT_CAPTURE_ENABLED_NOT_CHECKED: + final boolean reallyEnabled = isContentCaptureReallyEnabled(); + mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE + : CONTENT_CAPTURE_ENABLED_FALSE; + return reallyEnabled; + default: + Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled); + return false; + } + + } + + /** + * Checks (without caching) if content capture is enabled for this context. + */ + private boolean isContentCaptureReallyEnabled() { + // First check if context supports it, so it saves a service lookup when it doesn't + if (mContext.getContentCaptureOptions() == null) return false; + + final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext); + // Then check if it's enabled in the contex itself. + if (ccm == null || !ccm.isContentCaptureEnabled()) return false; + + return true; + } + + private void performContentCaptureInitialReport() { + mPerformContentCapture = false; // One-time offer! + final View rootView = mView; + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " + + getClass().getSimpleName()); + } + try { + if (!isContentCaptureEnabled()) return; + + // Content capture is a go! + rootView.dispatchInitialProvideContentCaptureStructure(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + + private void handleContentCaptureFlush() { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "handleContentCaptureFlush()"); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " + + getClass().getSimpleName()); + } + try { + if (!isContentCaptureEnabled()) return; + + final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; + if (ccm == null) { + Log.w(TAG, "No ContentCapture on AttachInfo"); + return; + } + ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } } private boolean draw(boolean fullRedrawNeeded) { @@ -3834,6 +3991,13 @@ public final class ViewRootImpl implements ViewParent, } } + void updateLocationInParentDisplay(int x, int y) { + if (mAttachInfo != null + && !mAttachInfo.mLocationInParentDisplay.equals(x, y)) { + mAttachInfo.mLocationInParentDisplay.set(x, y); + } + } + /** * Set the root-level system gesture exclusion rects. These are added to those provided by * the root's view hierarchy. @@ -4338,6 +4502,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_INSETS_CHANGED = 30; private static final int MSG_INSETS_CONTROL_CHANGED = 31; private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; + private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33; final class ViewRootHandler extends Handler { @Override @@ -4399,6 +4564,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_INSETS_CONTROL_CHANGED"; case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED"; + case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: + return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED"; } return super.getMessageName(message); } @@ -4634,6 +4801,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { systemGestureExclusionChanged(); } break; + case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED: { + updateLocationInParentDisplay(msg.arg1, msg.arg2); + } break; } } } @@ -7840,6 +8010,17 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + /** + * Dispatch the offset changed. + * + * @param offset the offset of this view in the parent window. + */ + public void dispatchLocationInParentDisplayChanged(Point offset) { + Message msg = + mHandler.obtainMessage(MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED, offset.x, offset.y); + mHandler.sendMessage(msg); + } + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { synchronized (this) { mWindowFocusChanged = true; @@ -8367,6 +8548,14 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void locationInParentDisplayChanged(Point offset) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchLocationInParentDisplayChanged(offset); + } + } + + @Override public void insetsChanged(InsetsState insetsState) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 9340b71a5280..bcc6a552f569 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -35,6 +35,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; +import android.content.Intent; import android.graphics.Insets; import android.graphics.Rect; import android.util.SparseArray; @@ -644,6 +645,14 @@ public final class WindowInsets { * {@link View#setSystemGestureExclusionRects} outside of the * {@link #getMandatorySystemGestureInsets() mandatory system gesture insets}. * + * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the + * exclusions it takes into account. The limit does not apply while the navigation + * bar is {@link View#SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the + * {@link android.inputmethodservice.InputMethodService input method} and + * {@link Intent#CATEGORY_HOME home activity}. + * </p> + * + * * <p>Simple taps are guaranteed to reach the window even within the system gesture insets, * as long as they are outside the {@link #getTappableElementInsets() system window insets}. * diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index a25f2eede905..2e5a7501f898 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -261,6 +261,13 @@ public interface WindowManager extends ViewManager { int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27; /** + * A display which can only contain one task is being shown because the first activity is + * started or it's being turned on. + * @hide + */ + int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28; + + /** * @hide */ @IntDef(prefix = { "TRANSIT_" }, value = { @@ -287,7 +294,8 @@ public interface WindowManager extends ViewManager { TRANSIT_TRANSLUCENT_ACTIVITY_OPEN, TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE, TRANSIT_CRASHING_ACTIVITY_CLOSE, - TRANSIT_TASK_CHANGE_WINDOWING_MODE + TRANSIT_TASK_CHANGE_WINDOWING_MODE, + TRANSIT_SHOW_SINGLE_TASK_DISPLAY }) @Retention(RetentionPolicy.SOURCE) @interface TransitionType {} @@ -311,6 +319,12 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4; /** + * Transition flag: Keyguard is going away with subtle animation. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION = 0x8; + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 46a59f09eca7..a22f5a576ab6 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -38,10 +38,11 @@ public interface WindowManagerPolicyConstants { int FLAG_INTERACTIVE = 0x20000000; int FLAG_PASS_TO_USER = 0x40000000; - // Flags for IActivityManager.keyguardGoingAway() + // Flags for IActivityTaskManager.keyguardGoingAway() int KEYGUARD_GOING_AWAY_FLAG_TO_SHADE = 1 << 0; int KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS = 1 << 1; int KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER = 1 << 2; + int KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS = 1 << 3; // Flags used for indicating whether the internal and/or external input devices // of some type are available. diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index c5a5f7360321..1e7440bd5a43 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -428,14 +428,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } final int flushFrequencyMs; - if (reason == FLUSH_REASON_IDLE_TIMEOUT) { - flushFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs; - } else if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) { + if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) { flushFrequencyMs = mManager.mOptions.textChangeFlushingFrequencyMs; } else { - Log.e(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): not called with a " - + "timeout reason."); - return; + if (reason != FLUSH_REASON_IDLE_TIMEOUT) { + if (sDebug) { + Log.d(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): not a timeout " + + "reason because mDirectServiceInterface is not ready yet"); + } + } + flushFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs; } mNextFlush = System.currentTimeMillis() + flushFrequencyMs; @@ -478,6 +480,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return; } + mNextFlushForTextChanged = false; + final int numberEvents = mEvents.size(); final String reasonString = getFlushReasonAsString(reason); if (sDebug) { @@ -493,10 +497,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { try { mHandler.removeMessages(MSG_FLUSH); - if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) { - mNextFlushForTextChanged = false; - } - final ParceledListSlice<ContentCaptureEvent> events = clearEvents(); mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions); } catch (RemoteException e) { diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 18ec334d0283..ac145b1d81a5 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -32,7 +32,7 @@ import android.net.Uri; * avoid unintentionally granting requests for new permissions, you should pass the * specific permissions you intend to grant to {@link #grant(String[]) grant()}, * and avoid writing code like this example: - * <pre> + * <pre class="prettyprint"> * permissionRequest.grant(permissionRequest.getResources()) // This is wrong!!! * </pre> * See the WebView's release notes for information about new protected resources. diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 95fe963d7891..4db630808ef1 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -519,15 +519,17 @@ public class WebChromeClient { * may not be supported and applications wishing to support these sources * or more advanced file operations should build their own Intent. * - * <pre> - * How to use: - * 1. Build an intent using {@link #createIntent} - * 2. Fire the intent using {@link android.app.Activity#startActivityForResult}. - * 3. Check for ActivityNotFoundException and take a user friendly action if thrown. - * 4. Listen the result using {@link android.app.Activity#onActivityResult} - * 5. Parse the result using {@link #parseResult} only if media capture was not requested. - * 6. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser} - * </pre> + * <p>How to use: + * <ol> + * <li>Build an intent using {@link #createIntent}</li> + * <li>Fire the intent using {@link android.app.Activity#startActivityForResult}.</li> + * <li>Check for ActivityNotFoundException and take a user friendly action if thrown.</li> + * <li>Listen the result using {@link android.app.Activity#onActivityResult}</li> + * <li>Parse the result using {@link #parseResult} only if media capture was not + * requested.</li> + * <li>Send the result using filePathCallback of {@link + * WebChromeClient#onShowFileChooser}</li> + * </ol> * * @return an Intent that supports basic file chooser sources. */ diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index a46580dcc539..18d4d691f726 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -464,7 +464,9 @@ public abstract class WebSettings { * Note that the feature will continue to be supported on older versions of * Android as before. * - * This function does not have any effect. + * @deprecated In Android O and afterwards, this function does not have + * any effect, the form data will be saved to platform's autofill service + * if applicable. */ @Deprecated public abstract void setSaveFormData(boolean save); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 14be73dec41c..95fca00f2346 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -413,6 +413,9 @@ public class WebView extends AbsoluteLayout if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); } + if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { + setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES); + } if (context == null) { throw new IllegalArgumentException("Invalid context argument"); @@ -759,7 +762,7 @@ public class WebView extends AbsoluteLayout * encoded. If the data is base64 encoded, the value of the encoding * parameter must be {@code "base64"}. HTML can be encoded with {@link * android.util.Base64#encodeToString(byte[],int)} like so: - * <pre> + * <pre class="prettyprint"> * String unencodedHtml = * "<html><body>'%28' is the code for '('</body></html>"; * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING); @@ -1851,7 +1854,7 @@ public class WebView extends AbsoluteLayout * important security note below for implications. * <p> Note that injected objects will not appear in JavaScript until the page is next * (re)loaded. JavaScript should be enabled before injecting the object. For example: - * <pre> + * <pre class="prettyprint"> * class JsObject { * {@literal @}JavascriptInterface * public String toString() { return "injectedObject"; } @@ -2803,6 +2806,12 @@ public class WebView extends AbsoluteLayout mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags); } + /** @hide */ + @Override + public void onProvideContentCaptureStructure(ViewStructure structure, int flags) { + mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags); + } + @Override public void autofill(SparseArray<AutofillValue>values) { mProvider.getViewDelegate().autofill(values); diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index c3e08fcf87d1..bbcba2e12a2c 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -91,6 +91,7 @@ public abstract class AbsSeekBar extends ProgressBar { @UnsupportedAppUsage private float mDisabledAlpha; + private int mThumbExclusionMaxSize; private int mScaledTouchSlop; private float mTouchDownX; @UnsupportedAppUsage @@ -171,6 +172,8 @@ public abstract class AbsSeekBar extends ProgressBar { applyTickMarkTint(); mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mThumbExclusionMaxSize = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.seekbar_thumb_exclusion_max_size); } /** @@ -763,12 +766,30 @@ public abstract class AbsSeekBar extends ProgressBar { } mGestureExclusionRects.clear(); thumb.copyBounds(mThumbRect); + mThumbRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop); + growRectTo(mThumbRect, Math.min(getHeight(), mThumbExclusionMaxSize)); mGestureExclusionRects.add(mThumbRect); mGestureExclusionRects.addAll(mUserGestureExclusionRects); super.setSystemGestureExclusionRects(mGestureExclusionRects); } /** + * Grows {@code r} from its center such that each dimension is at least {@code minimumSize}. + */ + private void growRectTo(Rect r, int minimumSize) { + int dy = (minimumSize - r.height()) / 2; + if (dy > 0) { + r.top -= dy; + r.bottom += dy; + } + int dx = (minimumSize - r.width()) / 2; + if (dx > 0) { + r.left -= dx; + r.right += dx; + } + } + + /** * @hide */ @Override diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index c3bb9a0201d0..c55f7d654548 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -1318,7 +1318,8 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @ViewStructureType int viewFor, int flags) { super.onProvideStructure(structure, viewFor, flags); - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL + || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { final Adapter adapter = getAdapter(); if (adapter == null) return; diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index c3c2c0db9a77..2bf1ba5cf017 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -50,7 +50,7 @@ import java.util.List; * override {@link #getView(int, View, ViewGroup)} * and inflate a view resource. * For a code example, see - * the <a href="https://developer.android.com/samples/CustomChoiceList/index.html"> + * the <a href="https://github.com/googlesamples/android-CustomChoiceList/#readme"> * CustomChoiceList</a> sample. * </p> * <p> diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index d985528c38fb..6b324a541c42 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -1202,13 +1202,13 @@ public class RelativeLayout extends ViewGroup { * determine where to position the view on the screen. If the view is not contained * within a relative layout, these attributes are ignored. * - * See the <a href="/guide/topics/ui/layout/relative.html"> - * Relative Layout</a> guide for example code demonstrating how to use relative layout’s + * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative + * Layout</a> guide for example code demonstrating how to use relative layout's * layout parameters in a layout XML. * * To learn more about layout parameters and how they differ from typical view attributes, - * see the <a href="/guide/topics/ui/declaring-layout.html#attributes"> - * Layouts guide</a>. + * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts + * guide</a>. * * * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 49a0f39b3bad..4c67b080252a 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -433,7 +433,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { * to the next tabbed view, in this example). * <p> * To move both the focus AND the selected tab at once, please use - * {@link #setCurrentTab}. Normally, the view logic takes care of + * {@link #focusCurrentTab}. Normally, the view logic takes care of * adjusting the focus, so unless you're circumventing the UI, * you'll probably just focus your interest here. * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 62598fce5947..0918c5fdefa8 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -162,6 +162,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; +import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -977,6 +979,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); } + if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) { + setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES); + } setTextInternal(""); @@ -10558,7 +10563,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Notify managers (such as {@link AutofillManager}) that are interested in text changes. + * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are + * interested on text changes. */ private void notifyListeningManagersAfterTextChanged() { @@ -10574,6 +10580,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener afm.notifyValueChanged(TextView.this); } } + + // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead + // of using isLaidout(), so it's not called in cases where it's laid out but a + // notifyAppeared was not sent. + + // ContentCapture + if (isLaidOut() && isImportantForContentCapture() && getNotifiedContentCaptureAppeared()) { + final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class); + if (cm != null && cm.isContentCaptureEnabled()) { + final ContentCaptureSession session = getContentCaptureSession(); + if (session != null) { + // TODO(b/111276913): pass flags when edited by user / add CTS test + session.notifyViewTextChanged(getAutofillId(), getText()); + } + } + } } private boolean isAutofillable() { @@ -11417,7 +11439,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL + || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId); } @@ -11433,8 +11456,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { + if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL + || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { if (mLayout == null) { + if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { + Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()"); + } assumeLayout(); } Layout layout = mLayout; @@ -11522,7 +11549,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (viewFor == VIEW_STRUCTURE_FOR_ASSIST) { + if (viewFor == VIEW_STRUCTURE_FOR_ASSIST + || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { // Extract style information that applies to the TextView as a whole. int style = 0; int typefaceStyle = getTypefaceStyle(); @@ -11550,7 +11578,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener structure.setTextStyle(getTextSize(), getCurrentTextColor(), AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style); } - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL + || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) { structure.setMinTextEms(getMinEms()); structure.setMaxTextEms(getMaxEms()); int maxLength = -1; |
