diff options
75 files changed, 2010 insertions, 790 deletions
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp index fcff58187e8d..45bb16184069 100644 --- a/ProtoLibraries.bp +++ b/ProtoLibraries.bp @@ -21,10 +21,6 @@ gensrcs { "soong_zip", ], - tool_files: [ - ":libprotobuf-internal-protos", - ], - cmd: "mkdir -p $(genDir)/$(in) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-javastream) " + @@ -42,6 +38,11 @@ gensrcs { "core/proto/**/*.proto", "libs/incident/**/*.proto", ], + + data: [ + ":libprotobuf-internal-protos", + ], + output_extension: "srcjar", } @@ -53,10 +54,6 @@ gensrcs { "protoc-gen-cppstream", ], - tool_files: [ - ":libprotobuf-internal-protos", - ], - cmd: "mkdir -p $(genDir) " + "&& $(location aprotoc) " + " --plugin=$(location protoc-gen-cppstream) " + @@ -73,6 +70,10 @@ gensrcs { "libs/incident/**/*.proto", ], + data: [ + ":libprotobuf-internal-protos", + ], + output_extension: "proto.h", } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java index ae86afbd8380..071707059f2d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java @@ -200,7 +200,10 @@ class JobNotificationCoordinator { // No more jobs using this notification. Apply the final job stop policy. // If the user attempted to stop the job/app, then always remove the notification // so the user doesn't get confused about the app state. + // Similarly, if the user background restricted the app, remove the notification so + // the user doesn't think the app is continuing to run in the background. if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE + || stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION || stopReason == JobParameters.STOP_REASON_USER) { mNotificationManagerInternal.cancelNotification( packageName, packageName, details.appUid, details.appPid, /* tag */ null, diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 5f795b6fedd9..109686d76b2f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()) .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; + boolean startedWithForegroundFlag = false; try { final Context.BindServiceFlags bindFlags; - if (job.shouldTreatAsUserInitiatedJob()) { + if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { + // If the user has bg restricted the app, don't give the job FG privileges + // such as bypassing data saver or getting the higher foreground proc state. + // If we've gotten to this point, the app is most likely in the foreground, + // so the job will run just fine while the user keeps the app in the foreground. bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); - } else if (job.shouldTreatAsExpeditedJob()) { + startedWithForegroundFlag = true; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND @@ -535,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; + // Wait until after bindService() returns a success value to set these so we don't + // have JobStatus objects that aren't running but have these set to true. job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob(); + job.startedWithForegroundFlag = startedWithForegroundFlag; return true; } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index ecee10a13c1d..25b3421a55f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.os.SystemClock; import android.os.UserHandle; @@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); - final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, - jobStatus.canRunInBatterySaver()); + final boolean isUserBgRestricted = + !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() + && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); + // If a job started with the foreground flag, it'll cause the UID to stay active + // and thus cause areJobsRestricted() to always return false, so if + // areJobsRestricted() returns false and the app is BG restricted and not TOP, + // we need to stop any jobs that started with the foreground flag so they don't + // keep the app in an elevated proc state. If we were to get in this situation, + // then the user restricted the app after the job started, so it's best to stop + // the job as soon as possible, especially since the job would be visible to the + // user (with a notification and in Task Manager). + // There are several other reasons that uidActive can be true for an app even if its + // proc state is less important than BFGS. + // JobScheduler has historically (at least up through UDC) allowed the app's jobs to run + // when its UID was active, even if it's background restricted. This has been fine because + // JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves + // will not keep the UID active. The logic here is to ensure that special jobs + // (e.g. user-initiated jobs) themselves do not keep the UID active when the app is + // background restricted. + final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag + && isUserBgRestricted + && mService.getUidProcState(uid) + > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + final boolean canRun = !shouldStopImmediately + && !mAppStateTracker.areJobsRestricted( + uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { @@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController { } boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun, - !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() - && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)); + isUserBgRestricted); didChange |= jobStatus.setUidActive(isActive); return didChange; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index f6bdb9303a04..6d938debde10 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); + if (mBackgroundMeteredAllowed.size() > 0) { + pw.print("Background metered allowed: "); + pw.println(mBackgroundMeteredAllowed); + pw.println(); + } + pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 3baa9e6d3de9..13903acc0439 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -430,6 +430,13 @@ public final class JobStatus { * when it started running. This isn't copied over when a job is rescheduled. */ public boolean startedAsUserInitiatedJob = false; + /** + * Whether this particular JobStatus instance started with the foreground flag + * (or more accurately, did <b>not</b> have the + * {@link android.content.Context#BIND_NOT_FOREGROUND} flag + * included in its binding flags when started). + */ + public boolean startedWithForegroundFlag = false; public boolean startedWithImmediacyPrivilege = false; @@ -1606,6 +1613,10 @@ public final class JobStatus { * for any reason. */ public boolean shouldTreatAsUserInitiatedJob() { + // isUserBgRestricted is intentionally excluded from this method. It should be fine to + // treat the job as a UI job while the app is TOP, but just not in the background. + // Instead of adding a proc state check here, the parts of JS that can make the distinction + // and care about the distinction can do the check. return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; @@ -1653,6 +1664,11 @@ public final class JobStatus { && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); } + /** Returns whether or not the app is background restricted by the user (FAS). */ + public boolean isUserBgRestricted() { + return mIsUserBgRestricted; + } + /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); @@ -2802,6 +2818,12 @@ public final class JobStatus { } pw.decreaseIndent(); + pw.print("Started with foreground flag: "); + pw.println(startedWithForegroundFlag); + if (mIsUserBgRestricted) { + pw.println("User BG restricted"); + } + if (changedAuthorities != null) { pw.println("Changed authorities:"); pw.increaseIndent(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 0ec3847d29f4..46260ea5e658 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -607,6 +607,7 @@ interface IActivityManager { void killPackageDependents(in String packageName, int userId); void makePackageIdle(String packageName, int userId); + void setDeterministicUidIdle(boolean deterministic); int getMemoryTrimLevel(); boolean isVrModePackageEnabled(in ComponentName packageName); void notifyLockedProfile(int userId); diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index ef3901082833..0527ed692da4 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -606,12 +606,9 @@ public final class UserHandle implements Parcelable { @Override public boolean equals(@Nullable Object obj) { - try { - if (obj != null) { - UserHandle other = (UserHandle)obj; - return mHandle == other.mHandle; - } - } catch (ClassCastException ignore) { + if (obj instanceof UserHandle) { + UserHandle other = (UserHandle) obj; + return mHandle == other.mHandle; } return false; } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index d87198a0dc85..ff7d8bb956ca 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -238,7 +238,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false"); DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, "true"); DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false"); - DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "true"); + DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false"); DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true"); DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true"); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ea750765799f..739c1bfccd3b 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2065,7 +2065,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get enabled autofill services status."); + throw new RuntimeException("Fail to get enabled autofill services status. " + e); } } @@ -2084,7 +2084,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill services component name."); + throw new RuntimeException("Fail to get autofill services component name. " + e); } } @@ -2111,7 +2111,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data id for field classification."); + throw new RuntimeException("Fail to get user data id for field classification. " + e); } } @@ -2134,7 +2134,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data for field classification."); + throw new RuntimeException("Fail to get user data for field classification. " + e); } } @@ -2174,7 +2174,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get field classification enabled status."); + throw new RuntimeException("Fail to get field classification enabled status. " + e); } } @@ -2198,7 +2198,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get default field classification algorithm."); + throw new RuntimeException("Fail to get default field classification algorithm. " + e); } } @@ -2220,7 +2220,8 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get available field classification algorithms."); + throw new + RuntimeException("Fail to get available field classification algorithms. " + e); } } @@ -2244,7 +2245,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill supported status."); + throw new RuntimeException("Fail to get autofill supported status. " + e); } } diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java index 1d5c6f1f12b1..6f229f6e0054 100644 --- a/core/java/android/webkit/SslErrorHandler.java +++ b/core/java/android/webkit/SslErrorHandler.java @@ -20,11 +20,15 @@ import android.annotation.SystemApi; import android.os.Handler; /** - * Represents a request for handling an SSL error. Instances of this class are - * created by the WebView and passed to - * {@link WebViewClient#onReceivedSslError}. The host application must call - * either {@link #proceed} or {@link #cancel} to set the WebView's response - * to the request. + * Represents a request for handling an SSL error. + * + * <p>A {@link WebView} creates an instance of this class. The instance is + * passed to {@link WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError)}. + * + * <p>The host application must call {@link #cancel()} or, contrary to secure + * web communication standards, {@link #proceed()} to provide the web view's + * response to the request. */ public class SslErrorHandler extends Handler { @@ -35,17 +39,29 @@ public class SslErrorHandler extends Handler { public SslErrorHandler() {} /** - * Proceed with the SSL certificate. - * <p> - * It is not recommended to proceed past SSL errors and this method should - * generally not be used; see {@link WebViewClient#onReceivedSslError} for - * more information. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to ignore the error and continue communicating with the server. + * + * <p class="warning"><b>Warning:</b> When an SSL error occurs, the host + * application should always call {@link #cancel()} rather than + * {@code proceed()} because an invalid SSL certificate means the connection + * is not secure. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void proceed() {} /** - * Cancel this request and all pending requests for the WebView that had - * the error. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to terminate communication with the server. Cancels the current server + * request and all pending requests for the {@code WebView}. + * + * <p>The host application must call this method to prevent a resource from + * loading when an SSL certificate is invalid. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void cancel() {} } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 55f09f110f88..2dfeae3567e5 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -382,30 +382,34 @@ public class WebViewClient { } /** - * Notify the host application that an SSL error occurred while loading a - * resource. The host application must call either {@link SslErrorHandler#cancel} or - * {@link SslErrorHandler#proceed}. Note that the decision may be retained for use in - * response to future SSL errors. The default behavior is to cancel the - * load. - * <p> - * This API is only called for recoverable SSL certificate errors. In the case of - * non-recoverable errors (such as when the server fails the client), WebView will call {@link - * #onReceivedError(WebView, WebResourceRequest, WebResourceError)} with {@link - * #ERROR_FAILED_SSL_HANDSHAKE}. - * <p> - * Applications are advised not to prompt the user about SSL errors, as - * the user is unlikely to be able to make an informed security decision - * and WebView does not provide any UI for showing the details of the + * Notifies the host application that an SSL error occurred while loading a + * resource. The host application must call either + * {@link SslErrorHandler#cancel()} or {@link SslErrorHandler#proceed()}. + * + * <p class="warning"><b>Warning:</b> Application overrides of this method + * can be used to display custom error pages or to silently log issues, but + * the host application should always call {@code SslErrorHandler#cancel()} + * and never proceed past errors. + * + * <p class="note"><b>Note:</b> Do not prompt the user about SSL errors. + * Users are unlikely to be able to make an informed security decision, and + * {@code WebView} does not provide a UI for showing the details of the * error in a meaningful way. - * <p> - * Application overrides of this method may display custom error pages or - * silently log issues, but it is strongly recommended to always call - * {@link SslErrorHandler#cancel} and never allow proceeding past errors. * - * @param view The WebView that is initiating the callback. - * @param handler An {@link SslErrorHandler} that will handle the user's - * response. - * @param error The SSL error object. + * <p>The decision to call {@code proceed()} or {@code cancel()} may be + * retained to facilitate responses to future SSL errors. The default + * behavior is to cancel the resource loading process. + * + * <p>This API is called only for recoverable SSL certificate errors. For + * non-recoverable errors (such as when the server fails the client), the + * {@code WebView} calls {@link #onReceivedError(WebView, + * WebResourceRequest, WebResourceError) onReceivedError(WebView, + * WebResourceRequest, WebResourceError)} with the + * {@link #ERROR_FAILED_SSL_HANDSHAKE} argument. + * + * @param view {@code WebView} that initiated the callback. + * @param handler {@link SslErrorHandler} that handles the user's response. + * @param error SSL error object. */ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index bf265689c0e0..4e7bfe50cd30 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -16,42 +16,50 @@ package com.android.internal.app; -import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; +import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.CombinedVibration; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.os.VibrationEffect; +import android.os.VibratorManager; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.widget.AnalogClock; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.R; import org.json.JSONObject; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.util.Random; /** * @hide @@ -59,30 +67,160 @@ import java.time.ZonedDateTime; public class PlatLogoActivity extends Activity { private static final String TAG = "PlatLogoActivity"; - private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s"; + private static final long LAUNCH_TIME = 5000L; + + private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u"; + + private static final float MIN_WARP = 1f; + private static final float MAX_WARP = 10f; // after all these years + private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false; - private SettableAnalogClock mClock; private ImageView mLogo; - private BubblesDrawable mBg; + private Starfield mStarfield; + + private FrameLayout mLayout; + + private TimeAnimator mAnim; + private ObjectAnimator mWarpAnim; + private Random mRandom; + private float mDp; + + private RumblePack mRumble; + + private boolean mAnimationsEnabled = true; + + private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + measureTouchPressure(event); + startWarp(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + stopWarp(); + break; + } + return true; + } + + }; + + private final Runnable mLaunchNextStage = () -> { + stopWarp(); + launchNextStage(false); + }; + + private final TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + mStarfield.update(deltaTime); + final float warpFrac = (mStarfield.getWarp() - MIN_WARP) / (MAX_WARP - MIN_WARP); + if (mAnimationsEnabled) { + mLogo.setTranslationX(mRandom.nextFloat() * warpFrac * 5 * mDp); + mLogo.setTranslationY(mRandom.nextFloat() * warpFrac * 5 * mDp); + } + if (warpFrac > 0f) { + mRumble.rumble(warpFrac); + } + mLayout.postInvalidate(); + } + }; + + private class RumblePack implements Handler.Callback { + private static final int MSG = 6464; + private static final int INTERVAL = 50; + + private final VibratorManager mVibeMan; + private final HandlerThread mVibeThread; + private final Handler mVibeHandler; + private boolean mSpinPrimitiveSupported; + + private long mLastVibe = 0; + + @SuppressLint("MissingPermission") + @Override + public boolean handleMessage(Message msg) { + final float warpFrac = msg.arg1 / 100f; + if (mSpinPrimitiveSupported) { + if (msg.getWhen() > mLastVibe + INTERVAL) { + mLastVibe = msg.getWhen(); + mVibeMan.vibrate(CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(PRIMITIVE_SPIN, (float) Math.pow(warpFrac, 3.0)) + .compose() + )); + } + } else { + if (mRandom.nextFloat() < warpFrac) { + mLogo.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + } + } + return false; + } + RumblePack() { + mVibeMan = getSystemService(VibratorManager.class); + mSpinPrimitiveSupported = mVibeMan.getDefaultVibrator() + .areAllPrimitivesSupported(PRIMITIVE_SPIN); + + mVibeThread = new HandlerThread("VibratorThread"); + mVibeThread.start(); + mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this); + } + + public void destroy() { + mVibeThread.quit(); + } + + private void rumble(float warpFrac) { + if (!mVibeThread.isAlive()) return; + + final Message msg = Message.obtain(); + msg.what = MSG; + msg.arg1 = (int) (warpFrac * 100); + mVibeHandler.removeMessages(MSG); + mVibeHandler.sendMessage(msg); + } + + } @Override - protected void onPause() { - super.onPause(); + protected void onDestroy() { + mRumble.destroy(); + + super.onDestroy(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setDecorFitsSystemWindows(false); getWindow().setNavigationBarColor(0); getWindow().setStatusBarColor(0); + getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); final ActionBar ab = getActionBar(); if (ab != null) ab.hide(); - final FrameLayout layout = new FrameLayout(this); + try { + mAnimationsEnabled = Settings.Global.getFloat(getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE) > 0f; + } catch (Settings.SettingNotFoundException e) { + mAnimationsEnabled = true; + } - mClock = new SettableAnalogClock(this); + mRumble = new RumblePack(); + + mLayout = new FrameLayout(this); + mRandom = new Random(); + mDp = getResources().getDisplayMetrics().density; + mStarfield = new Starfield(mRandom, mDp * 2f); + mStarfield.setVelocity( + 200f * (mRandom.nextFloat() - 0.5f), + 200f * (mRandom.nextFloat() - 0.5f)); + mLayout.setBackground(mStarfield); final DisplayMetrics dm = getResources().getDisplayMetrics(); final float dp = dm.density; @@ -90,22 +228,79 @@ public class PlatLogoActivity extends Activity { final int widgetSize = (int) (minSide * 0.75); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize); lp.gravity = Gravity.CENTER; - layout.addView(mClock, lp); mLogo = new ImageView(this); - mLogo.setVisibility(View.GONE); mLogo.setImageResource(R.drawable.platlogo); - layout.addView(mLogo, lp); + mLogo.setOnTouchListener(mTouchListener); + mLogo.requestFocus(); + mLayout.addView(mLogo, lp); + + Log.v(TAG, "Hello"); + + setContentView(mLayout); + } + + private void startAnimating() { + mAnim = new TimeAnimator(); + mAnim.setTimeListener(mTimeListener); + mAnim.start(); + } + + private void stopAnimating() { + mAnim.cancel(); + mAnim = null; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + if (event.getRepeatCount() == 0) { + startWarp(); + } + return true; + } + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + stopWarp(); + return true; + } + return false; + } - mBg = new BubblesDrawable(); - mBg.setLevel(0); - mBg.avoid = widgetSize / 2; - mBg.padding = 0.5f * dp; - mBg.minR = 1 * dp; - layout.setBackground(mBg); - layout.setOnLongClickListener(mBg); + private void startWarp() { + stopWarp(); + mWarpAnim = ObjectAnimator.ofFloat(mStarfield, "warp", MIN_WARP, MAX_WARP) + .setDuration(LAUNCH_TIME); + mWarpAnim.start(); + + mLogo.postDelayed(mLaunchNextStage, LAUNCH_TIME + 1000L); + } - setContentView(layout); + private void stopWarp() { + if (mWarpAnim != null) { + mWarpAnim.cancel(); + mWarpAnim.removeAllListeners(); + mWarpAnim = null; + } + mStarfield.setWarp(1f); + mLogo.removeCallbacks(mLaunchNextStage); + } + + @Override + public void onResume() { + super.onResume(); + startAnimating(); + } + + @Override + public void onPause() { + stopWarp(); + stopAnimating(); + super.onPause(); } private boolean shouldWriteSettings() { @@ -113,38 +308,14 @@ public class PlatLogoActivity extends Activity { } private void launchNextStage(boolean locked) { - mClock.animate() - .alpha(0f).scaleX(0.5f).scaleY(0.5f) - .withEndAction(() -> mClock.setVisibility(View.GONE)) - .start(); - - mLogo.setAlpha(0f); - mLogo.setScaleX(0.5f); - mLogo.setScaleY(0.5f); - mLogo.setVisibility(View.VISIBLE); - mLogo.animate() - .alpha(1f) - .scaleX(1f) - .scaleY(1f) - .setInterpolator(new OvershootInterpolator()) - .start(); - - mLogo.postDelayed(() -> { - final ObjectAnimator anim = ObjectAnimator.ofInt(mBg, "level", 0, 10000); - anim.setInterpolator(new DecelerateInterpolator(1f)); - anim.start(); - }, - 500 - ); - final ContentResolver cr = getContentResolver(); try { if (shouldWriteSettings()) { - Log.v(TAG, "Saving egg unlock=" + locked); + Log.v(TAG, "Saving egg locked=" + locked); syncTouchPressure(); Settings.System.putLong(cr, - S_EGG_UNLOCK_SETTING, + U_EGG_UNLOCK_SETTING, locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { @@ -152,14 +323,18 @@ public class PlatLogoActivity extends Activity { } try { - startActivity(new Intent(Intent.ACTION_MAIN) + final Intent eggActivity = new Intent(Intent.ACTION_MAIN) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .addCategory("com.android.internal.category.PLATLOGO")); + .addCategory("com.android.internal.category.PLATLOGO"); + Log.v(TAG, "launching: " + eggActivity); + startActivity(eggActivity); } catch (ActivityNotFoundException ex) { Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } - //finish(); // no longer finish upon unlock; it's fun to frob the dial + if (FINISH_AFTER_NEXT_STAGE_LAUNCH) { + finish(); // we're done here. + } } static final String TOUCH_STATS = "touch.stats"; @@ -217,266 +392,111 @@ public class PlatLogoActivity extends Activity { super.onStop(); } - /** - * Subclass of AnalogClock that allows the user to flip up the glass and adjust the hands. - */ - public class SettableAnalogClock extends AnalogClock { - private int mOverrideHour = -1; - private int mOverrideMinute = -1; - private boolean mOverride = false; + private static class Starfield extends Drawable { + private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE + + private static final int NUM_PLANES = 2; + private final float[] mStars = new float[NUM_STARS * 4]; + private float mVx, mVy; + private long mDt = 0; + private final Paint mStarPaint; - public SettableAnalogClock(Context context) { - super(context); + private final Random mRng; + private final float mSize; + + private final Rect mSpace = new Rect(); + private float mWarp = 1f; + + private float mBuffer; + + public void setWarp(float warp) { + mWarp = warp; } - @Override - protected Instant now() { - final Instant realNow = super.now(); - final ZoneId tz = Clock.systemDefaultZone().getZone(); - final ZonedDateTime zdTime = realNow.atZone(tz); - if (mOverride) { - if (mOverrideHour < 0) { - mOverrideHour = zdTime.getHour(); - } - return Clock.fixed(zdTime - .withHour(mOverrideHour) - .withMinute(mOverrideMinute) - .withSecond(0) - .toInstant(), tz).instant(); - } else { - return realNow; - } + public float getWarp() { + return mWarp; } - double toPositiveDegrees(double rad) { - return (Math.toDegrees(rad) + 360 - 90) % 360; + Starfield(Random rng, float size) { + mRng = rng; + mSize = size; + mStarPaint = new Paint(); + mStarPaint.setStyle(Paint.Style.STROKE); + mStarPaint.setColor(Color.WHITE); } @Override - public boolean onTouchEvent(MotionEvent ev) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mOverride = true; - // pass through - case MotionEvent.ACTION_MOVE: - measureTouchPressure(ev); - - float x = ev.getX(); - float y = ev.getY(); - float cx = getWidth() / 2f; - float cy = getHeight() / 2f; - float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy)); - - int minutes = (75 - (int) (angle / 6)) % 60; - int minuteDelta = minutes - mOverrideMinute; - if (minuteDelta != 0) { - if (Math.abs(minuteDelta) > 45 && mOverrideHour >= 0) { - int hourDelta = (minuteDelta < 0) ? 1 : -1; - mOverrideHour = (mOverrideHour + 24 + hourDelta) % 24; - } - mOverrideMinute = minutes; - if (mOverrideMinute == 0) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - if (getScaleX() == 1f) { - setScaleX(1.05f); - setScaleY(1.05f); - animate().scaleX(1f).scaleY(1f).setDuration(150).start(); - } - } else { - performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); - } - - onTimeChanged(); - postInvalidate(); - } - - return true; - case MotionEvent.ACTION_UP: - if (mOverrideMinute == 0 && (mOverrideHour % 12) == 1) { - Log.v(TAG, "13:00"); - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - launchNextStage(false); - } - return true; + public void onBoundsChange(Rect bounds) { + mSpace.set(bounds); + mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP; + mSpace.inset(-(int) mBuffer, -(int) mBuffer); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + mStars[4 * i] = mRng.nextFloat() * w; + mStars[4 * i + 1] = mRng.nextFloat() * h; + mStars[4 * i + 2] = mStars[4 * i]; + mStars[4 * i + 3] = mStars[4 * i + 1]; } - return false; } - } - private static final String[][] EMOJI_SETS = { - {"🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑", - "🍒", "🍓", "🫐", "🥝"}, - {"😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"}, - {"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "🫠", "😉", "😊", - "😇", "🥰", "😍", "🤩", "😘", "😗", "☺️", "😚", "😙", "🥲", "😋", "😛", "😜", - "🤪", "😝", "🤑", "🤗", "🤭", "🫢", "🫣", "🤫", "🤔", "🫡", "🤐", "🤨", "😐", - "😑", "😶", "🫥", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪", "🤤", "😴", - "😷"}, - { "🤩", "😍", "🥰", "😘", "🥳", "🥲", "🥹" }, - { "🫠" }, - {"💘", "💝", "💖", "💗", "💓", "💞", "💕", "❣", "💔", "❤", "🧡", "💛", - "💚", "💙", "💜", "🤎", "🖤", "🤍"}, - // {"👁", "️🫦", "👁️"}, // this one is too much - {"👽", "🛸", "✨", "🌟", "💫", "🚀", "🪐", "🌙", "⭐", "🌍"}, - {"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"}, - {"🐙", "🪸", "🦑", "🦀", "🦐", "🐡", "🦞", "🐠", "🐟", "🐳", "🐋", "🐬", "🫧", "🌊", - "🦈"}, - {"🙈", "🙉", "🙊", "🐵", "🐒"}, - {"♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"}, - {"🕛", "🕧", "🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡", - "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦"}, - {"🌺", "🌸", "💮", "🏵️", "🌼", "🌿"}, - {"🐢", "✨", "🌟", "👑"} - }; - - static class Bubble { - public float x, y, r; - public int color; - public String text = null; - } - - class BubblesDrawable extends Drawable implements View.OnLongClickListener { - private static final int MAX_BUBBS = 2000; - - private final int[] mColorIds = { - android.R.color.system_accent3_400, - android.R.color.system_accent3_500, - android.R.color.system_accent3_600, - - android.R.color.system_accent2_400, - android.R.color.system_accent2_500, - android.R.color.system_accent2_600, - }; - - private int[] mColors = new int[mColorIds.length]; - - private int mEmojiSet = -1; - - private final Bubble[] mBubbs = new Bubble[MAX_BUBBS]; - private int mNumBubbs; - - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - public float avoid = 0f; - public float padding = 0f; - public float minR = 0f; - - BubblesDrawable() { - for (int i = 0; i < mColorIds.length; i++) { - mColors[i] = getColor(mColorIds[i]); - } - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j] = new Bubble(); - } + public void setVelocity(float x, float y) { + mVx = x; + mVy = y; } @Override - public void draw(Canvas canvas) { - if (getLevel() == 0) return; - final float f = getLevel() / 10000f; - mPaint.setStyle(Paint.Style.FILL); - mPaint.setTextAlign(Paint.Align.CENTER); - int drawn = 0; - for (int j = 0; j < mNumBubbs; j++) { - if (mBubbs[j].color == 0 || mBubbs[j].r == 0) continue; - if (mBubbs[j].text != null) { - mPaint.setTextSize(mBubbs[j].r * 1.75f); - canvas.drawText(mBubbs[j].text, mBubbs[j].x, - mBubbs[j].y + mBubbs[j].r * f * 0.6f, mPaint); - } else { - mPaint.setColor(mBubbs[j].color); - canvas.drawCircle(mBubbs[j].x, mBubbs[j].y, mBubbs[j].r * f, mPaint); + public void draw(@NonNull Canvas canvas) { + final float dtSec = mDt / 1000f; + final float dx = (mVx * dtSec * mWarp); + final float dy = (mVy * dtSec * mWarp); + + final boolean inWarp = mWarp > 1f; + + canvas.drawColor(Color.BLACK); // 0xFF16161D); + + if (mDt > 0 && mDt < 1000) { + canvas.translate( + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f), + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f) + ); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1; + mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w; + mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h; + mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100; + mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100; } - drawn++; } - } - - public void chooseEmojiSet() { - mEmojiSet = (int) (Math.random() * EMOJI_SETS.length); - final String[] emojiSet = EMOJI_SETS[mEmojiSet]; - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j].text = emojiSet[(int) (Math.random() * emojiSet.length)]; + final int slice = (mStars.length / NUM_PLANES / 4) * 4; + for (int p = 0; p < NUM_PLANES; p++) { + mStarPaint.setStrokeWidth(mSize * (p + 1)); + if (inWarp) { + canvas.drawLines(mStars, p * slice, slice, mStarPaint); + } + canvas.drawPoints(mStars, p * slice, slice, mStarPaint); } - invalidateSelf(); } @Override - protected boolean onLevelChange(int level) { - invalidateSelf(); - return true; - } + public void setAlpha(int alpha) { - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - randomize(); - } - - private void randomize() { - final float w = getBounds().width(); - final float h = getBounds().height(); - final float maxR = Math.min(w, h) / 3f; - mNumBubbs = 0; - if (avoid > 0f) { - mBubbs[mNumBubbs].x = w / 2f; - mBubbs[mNumBubbs].y = h / 2f; - mBubbs[mNumBubbs].r = avoid; - mBubbs[mNumBubbs].color = 0; - mNumBubbs++; - } - for (int j = 0; j < MAX_BUBBS; j++) { - // a simple but time-tested bubble-packing algorithm: - // 1. pick a spot - // 2. shrink the bubble until it is no longer overlapping any other bubble - // 3. if the bubble hasn't popped, keep it - int tries = 5; - while (tries-- > 0) { - float x = (float) Math.random() * w; - float y = (float) Math.random() * h; - float r = Math.min(Math.min(x, w - x), Math.min(y, h - y)); - - // shrink radius to fit other bubbs - for (int i = 0; i < mNumBubbs; i++) { - r = (float) Math.min(r, - Math.hypot(x - mBubbs[i].x, y - mBubbs[i].y) - mBubbs[i].r - - padding); - if (r < minR) break; - } - - if (r >= minR) { - // we have found a spot for this bubble to live, let's save it and move on - r = Math.min(maxR, r); - - mBubbs[mNumBubbs].x = x; - mBubbs[mNumBubbs].y = y; - mBubbs[mNumBubbs].r = r; - mBubbs[mNumBubbs].color = mColors[(int) (Math.random() * mColors.length)]; - mNumBubbs++; - break; - } - } - } - Log.v(TAG, String.format("successfully placed %d bubbles (%d%%)", - mNumBubbs, (int) (100f * mNumBubbs / MAX_BUBBS))); } @Override - public void setAlpha(int alpha) { } + public void setColorFilter(@Nullable ColorFilter colorFilter) { - @Override - public void setColorFilter(ColorFilter colorFilter) { } + } @Override public int getOpacity() { - return TRANSLUCENT; + return PixelFormat.OPAQUE; } - @Override - public boolean onLongClick(View v) { - if (getLevel() == 0) return false; - chooseEmojiSet(); - return true; + public void update(long dt) { + mDt = dt; } } - -} +}
\ No newline at end of file diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 8135f9cd2f46..dd52de4f84c7 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -118,6 +118,9 @@ public final class SystemUiDeviceConfigFlags { */ public static final String NAS_DEFAULT_SERVICE = "nas_default_service"; + /** (boolean) Whether notify() calls to NMS should acquire and hold WakeLocks. */ + public static final String NOTIFY_WAKELOCK = "nms_notify_wakelock"; + // Flags related to media notifications /** diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index da214868ca05..f3acab063bbf 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -13,33 +13,186 @@ Copyright (C) 2021 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<vector android:height="128dp" - android:width="128dp" - android:viewportHeight="24" - android:viewportWidth="24" - xmlns:android="http://schemas.android.com/apk/res/android"> - <path - android:fillColor="@android:color/system_accent1_400" - android:pathData="M11,0.3c0.6,-0.3 1.4,-0.3 2,0l0.6,0.4c0.7,0.4 1.4,0.6 2.2,0.6l0.7,-0.1c0.7,0 1.4,0.3 1.8,0.9l0.3,0.6c0.4,0.7 1,1.2 1.7,1.5L21,4.5c0.7,0.3 1.1,0.9 1.2,1.7v0.7C22.2,7.7 22.5,8.4 23,9l0.4,0.5c0.4,0.6 0.5,1.3 0.2,2l-0.3,0.6c-0.3,0.7 -0.4,1.5 -0.3,2.3l0.1,0.7c0.1,0.7 -0.2,1.4 -0.7,1.9L22,17.5c-0.6,0.5 -1.1,1.1 -1.3,1.9L20.5,20c-0.2,0.7 -0.8,1.2 -1.5,1.4l-0.7,0.1c-0.8,0.2 -1.4,0.5 -2,1.1l-0.5,0.5c-0.5,0.5 -1.3,0.7 -2,0.5l-0.6,-0.2c-0.8,-0.2 -1.5,-0.2 -2.3,0l-0.6,0.2c-0.7,0.2 -1.5,0 -2,-0.5l-0.5,-0.5c-0.5,-0.5 -1.2,-0.9 -2,-1.1L5,21.4c-0.7,-0.2 -1.3,-0.7 -1.5,-1.4l-0.2,-0.7C3.1,18.6 2.6,18 2,17.5l-0.6,-0.4c-0.6,-0.5 -0.8,-1.2 -0.7,-1.9l0.1,-0.7c0.1,-0.8 0,-1.6 -0.3,-2.3l-0.3,-0.6c-0.3,-0.7 -0.2,-1.4 0.2,-2L1,9c0.5,-0.6 0.7,-1.4 0.8,-2.2V6.2C1.9,5.5 2.3,4.8 3,4.5l0.6,-0.3c0.7,-0.3 1.3,-0.9 1.7,-1.5l0.3,-0.6c0.4,-0.6 1.1,-1 1.8,-0.9l0.7,0.1c0.8,0 1.6,-0.2 2.2,-0.6L11,0.3z" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="512dp" + android:height="512dp" + android:viewportWidth="512" + android:viewportHeight="512"> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"> + <aapt:attr name="android:fillColor"> + <gradient + android:startX="256" + android:startY="21.81" + android:endX="256" + android:endY="350.42" + android:type="linear"> + <item android:offset="0" android:color="#FF073042"/> + <item android:offset="1" android:color="#FF073042"/> + </gradient> + </aapt:attr> + </path> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M171.92,216.82h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,275.73h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M369.04,337.63h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M285.93,252.22h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M318.96,218.84h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M294.05,288.55h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M330.82,273.31h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,298.95h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M220.14,238.94h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M272.1,318.9h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M293.34,349.25h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M161.05,254.24h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M378.92,192h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M137.87,323.7h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0" + android:strokeWidth="56.561" + android:fillColor="#00000000" + android:strokeColor="#f86734"/> + <path + android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z" + android:fillColor="#3ddc84"/> + <path + android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z" + android:fillColor="#fff"/> + <path + android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z" + android:fillColor="#fff"/> + <path + android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z" + android:fillColor="#fff"/> + <path + android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z" + android:fillColor="#fff"/> + <path + android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z" + android:fillColor="#fff"/> + <path + android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z" + android:fillColor="#fff"/> + <path + android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z" + android:fillColor="#fff"/> + <path + android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z" + android:fillColor="#fff"/> + <path + android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z" + android:fillColor="#fff"/> </vector> + diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 8635c56b7bc6..d902fd49ba60 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -93,62 +93,34 @@ <style name="RestartDialogTitleText"> <item name="android:textSize">24sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">8sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> </style> - <style name="RestartDialogBodyText"> + <style name="RestartDialogBodyStyle"> <item name="android:textSize">14sp</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + </style> + + <style name="RestartDialogBodyText" parent="RestartDialogBodyStyle"> <item name="android:letterSpacing">0.02</item> <item name="android:textColor">?android:attr/textColorSecondary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogCheckboxText"> - <item name="android:textSize">16sp</item> + <style name="RestartDialogCheckboxText" parent="RestartDialogBodyStyle"> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">4sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogDismissButton"> + <style name="RestartDialogDismissButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> - <style name="RestartDialogConfirmButton"> + <style name="RestartDialogConfirmButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> <style name="ReachabilityEduHandLayout" parent="Theme.AppCompat.Light"> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index ab8e7e63ef7f..f70d3aec9ec8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -63,8 +63,10 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.InteractionJankMonitorUtils; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; @@ -104,6 +106,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final Rect mWinBounds2 = new Rect(); private final SplitLayoutHandler mSplitLayoutHandler; private final SplitWindowManager mSplitWindowManager; + private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; private final ResizingEffectPolicy mSurfaceEffectPolicy; @@ -128,13 +131,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public SplitLayout(String windowName, Context context, Configuration configuration, SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, - int parallaxType) { + DisplayController displayController, DisplayImeController displayImeController, + ShellTaskOrganizer taskOrganizer, int parallaxType) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); mDensity = configuration.densityDpi; mSplitLayoutHandler = splitLayoutHandler; + mDisplayController = displayController; mDisplayImeController = displayImeController; mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration, parentContainerCallbacks); @@ -145,7 +149,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange updateDividerConfig(mContext); mRootBounds.set(configuration.windowConfiguration.getBounds()); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); resetDividerPosition(); mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide); @@ -314,7 +318,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mRotation = rotation; mDensity = density; mUiMode = uiMode; - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); updateDividerConfig(mContext); initDividerPosition(mTempRect); updateInvisibleRect(); @@ -324,7 +328,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Rotate the layout to specific rotation and calculate new bounds. The stable insets value * should be calculated by display layout. */ - public void rotateTo(int newRotation, Rect stableInsets) { + public void rotateTo(int newRotation) { final int rotationDelta = (newRotation - mRotation + 4) % 4; final boolean changeOrient = (rotationDelta % 2) != 0; @@ -337,7 +341,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // We only need new bounds here, other configuration should be update later. mTempRect.set(mRootBounds); mRootBounds.set(tmpRect); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, stableInsets); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); initDividerPosition(mTempRect); } @@ -548,10 +552,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss); } - private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds, - @Nullable Rect stableInsets) { + private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) { final boolean isLandscape = isLandscape(rootBounds); - final Rect insets = stableInsets != null ? stableInsets : getDisplayInsets(context); + final Rect insets = getDisplayStableInsets(context); // Make split axis insets value same as the larger one to avoid bounds1 and bounds2 // have difference for avoiding size-compat mode when switching unresizable apps in @@ -634,7 +637,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, Consumer<Rect> finishCallback) { final boolean isLandscape = isLandscape(); - final Rect insets = getDisplayInsets(mContext); + final Rect insets = getDisplayStableInsets(mContext); insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top, isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom); @@ -705,13 +708,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return animator; } - private static Rect getDisplayInsets(Context context) { - return context.getSystemService(WindowManager.class) - .getMaximumWindowMetrics() - .getWindowInsets() - .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() - | WindowInsets.Type.displayCutout()) - .toRect(); + private Rect getDisplayStableInsets(Context context) { + final DisplayLayout displayLayout = + mDisplayController.getDisplayLayout(context.getDisplayId()); + return displayLayout != null + ? displayLayout.stableInsets() + : context.getSystemService(WindowManager.class) + .getMaximumWindowMetrics() + .getWindowInsets() + .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() + | WindowInsets.Type.displayCutout()) + .toRect(); } private static boolean isLandscape(Rect bounds) { @@ -784,7 +791,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private int getSmallestWidthDp(Rect bounds) { mTempRect.set(bounds); - mTempRect.inset(getDisplayInsets(mContext)); + mTempRect.inset(getDisplayStableInsets(mContext)); final int minWidth = Math.min(mTempRect.width(), mTempRect.height()); final float density = mContext.getResources().getDisplayMetrics().density; return (int) (minWidth / density); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 18898f1f2153..07d11cf9f63c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -534,17 +534,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } - if (ENABLE_SHELL_TRANSITIONS) { - if (requestEnterSplit && mSplitScreenOptional.isPresent()) { - mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, - isPipTopLeft() - ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); - mPipTransitionController.startExitTransition( - TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */); - return; - } - } - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); final Rect destinationBounds = new Rect(displayBounds); final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) @@ -553,10 +542,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // For exiting to fullscreen, the windowing mode of task will be changed to fullscreen // until the animation is finished. Otherwise if the activity is resumed and focused at the // begin of aniamtion, the app may do something too early to distub the animation. - final boolean toFullscreen = destinationBounds.equals(displayBounds); - if (Transitions.SHELL_TRANSITIONS_ROTATION || (Transitions.ENABLE_SHELL_TRANSITIONS - && !toFullscreen)) { + if (Transitions.SHELL_TRANSITIONS_ROTATION) { // When exit to fullscreen with Shell transition enabled, we update the Task windowing // mode directly so that it can also trigger display rotation and visibility update in // the same transition if there will be any. @@ -588,9 +575,29 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); if (Transitions.ENABLE_SHELL_TRANSITIONS) { + if (requestEnterSplit && mSplitScreenOptional.isPresent()) { + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, + isPipToTopLeft() + ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); + mPipTransitionController.startExitTransition( + TRANSIT_EXIT_PIP_TO_SPLIT, wct, destinationBounds); + return; + } + + if (mSplitScreenOptional.isPresent()) { + // If pip activity will reparent to origin task case and if the origin task still + // under split root, apply exit split transaction to make it expand to fullscreen. + SplitScreenController split = mSplitScreenOptional.get(); + if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) { + split.prepareExitSplitScreen(wct, split.getStageOfTask( + mTaskInfo.lastParentTaskIdBeforePip)); + } + } mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); return; } + if (mSplitScreenOptional.isPresent()) { // If pip activity will reparent to origin task case and if the origin task still under // split root, just exit split screen here to ensure it could expand to fullscreen. @@ -1666,17 +1673,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - private boolean isPipTopLeft() { - if (!mSplitScreenOptional.isPresent()) { - return false; - } - final Rect topLeft = new Rect(); - final Rect bottomRight = new Rect(); - mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight); - - return topLeft.contains(mPipBoundsState.getBounds()); - } - private boolean isPipToTopLeft() { if (!mSplitScreenOptional.isPresent()) { return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index bf75132dde54..fb966c7de925 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -989,12 +989,7 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TaskInfo taskInfo) { - final int changeSize = info.getChanges().size(); - if (changeSize < 4) { - throw new RuntimeException( - "Got an exit-pip-to-split transition with unexpected change-list"); - } - for (int i = changeSize - 1; i >= 0; i--) { + for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change change = info.getChanges().get(i); final int mode = change.getMode(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 2fff0e469f3d..e1bcd70c256b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -38,6 +38,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -223,6 +224,13 @@ public abstract class PipTransitionController implements Transitions.TransitionH return false; } + /** Whether a particular package is same as current pip package. */ + public boolean isInPipPackage(String packageName) { + final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); + return packageName != null && inPipTask != null + && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); + } + /** Add PiP-related changes to `outWCT` for the given request. */ public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index ea33a1f1b56d..e7a367f1992d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -88,6 +88,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -332,6 +333,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED; } + /** Get the split stage of task is under it. */ + public @StageType int getStageOfTask(int taskId) { + return mStageCoordinator.getStageOfTask(taskId); + } + /** Check split is foreground and task is under split or not by taskId. */ public boolean isTaskInSplitScreenForeground(int taskId) { return isTaskInSplitScreen(taskId) && isSplitScreenVisible(); @@ -378,17 +384,35 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } - public void enterSplitScreen(int taskId, boolean leftOrTop) { - enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); - } - + /** + * Doing necessary window transaction for other transition handler need to enter split in + * transition. + */ public void prepareEnterSplitScreen(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo, int startPosition) { - mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition); + mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition, + false /* resizeAnim */); + } + + /** + * Doing necessary surface transaction for other transition handler need to enter split in + * transition when finished. + */ + public void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { + mStageCoordinator.finishEnterSplitScreen(finishT); } - public void finishEnterSplitScreen(SurfaceControl.Transaction t) { - mStageCoordinator.finishEnterSplitScreen(t); + /** + * Doing necessary window transaction for other transition handler need to exit split in + * transition. + */ + public void prepareExitSplitScreen(WindowContainerTransaction wct, + @StageType int stageToTop) { + mStageCoordinator.prepareExitSplitScreen(stageToTop, wct); + } + + public void enterSplitScreen(int taskId, boolean leftOrTop) { + enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); } public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 986309948ada..d21f8a48e62a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -199,19 +199,7 @@ class SplitScreenTransitions { boolean isOpening = TransitionUtil.isOpeningType(info.getType()); if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out - if (change.getSnapshot() != null) { - // This case is happened if task is going to reparent to TDA, the origin leash - // doesn't rendor so we use snapshot to replace it animating. - t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); - // Use origin leash layer. - t.setLayer(change.getSnapshot(), info.getChanges().size() - i); - t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left, - change.getStartAbsBounds().top); - t.show(change.getSnapshot()); - startFadeAnimation(change.getSnapshot(), false /* show */); - } else { - startFadeAnimation(leash, false /* show */); - } + startFadeAnimation(leash, false /* show */); } else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) { t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); // Ensure snapshot it on the top of all transition surfaces diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index a1f82aded3c7..fd871ed8c8ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -122,7 +122,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -171,7 +170,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); - private final DisplayLayout mDisplayLayout; @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -311,7 +309,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, this::onTransitionAnimationComplete, this); mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId)); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -345,7 +342,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainExecutor = mainExecutor; mRecentTasks = recentTasks; mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -393,7 +389,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { - prepareEnterSplitScreen(wct, task, stagePosition); + prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */); if (ENABLE_SHELL_TRANSITIONS) { mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, @@ -491,20 +487,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Launches an activity into split. */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { + mSplitRequest = new SplitRequest(intent.getIntent(), position); if (!ENABLE_SHELL_TRANSITIONS) { startIntentLegacy(intent, fillInIntent, position, options); return; } final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); wct.sendPendingIntent(intent, fillInIntent, options); + // If this should be mixed, just send the intent to avoid split handle transition directly. + if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) { + mTaskOrganizer.applyTransaction(wct); + return; + } + // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; - prepareEnterSplitScreen(wct, null /* taskInfo */, position); + prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, extraTransitType, !mIsDropEntering); @@ -561,7 +563,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isEnteringSplit && mLogger.isEnterRequestedByDrag()) { updateWindowBounds(mSplitLayout, wct); } - mSplitRequest = new SplitRequest(intent.getIntent(), position); wct.sendPendingIntent(intent, fillInIntent, options); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } @@ -1449,7 +1450,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * an existing WindowContainerTransaction (rather than applying immediately). This is intended * to be used when exiting split might be bundled with other window operations. */ - private void prepareExitSplitScreen(@StageType int stageToTop, + void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); @@ -1457,7 +1458,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { - prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED); + prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED, + !mIsDropEntering); } /** @@ -1465,17 +1467,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * into side stage. */ void prepareEnterSplitScreen(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { onSplitScreenEnter(); if (isSplitActive()) { - prepareBringSplit(wct, taskInfo, startPosition); + prepareBringSplit(wct, taskInfo, startPosition, resizeAnim); } else { - prepareActiveSplit(wct, taskInfo, startPosition); + prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim); } } private void prepareBringSplit(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { if (taskInfo != null) { wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); @@ -1487,12 +1491,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // won't guarantee to put the task to the indicated new position. mMainStage.evictAllChildren(wct); mMainStage.reparentTopTask(wct); - prepareSplitLayout(wct); + prepareSplitLayout(wct, resizeAnim); } } private void prepareActiveSplit(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { if (!ENABLE_SHELL_TRANSITIONS) { // Legacy transition we need to create divider here, shell transition case we will // create it on #finishEnterSplitScreen @@ -1503,17 +1508,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.addTask(taskInfo, wct); } mMainStage.activate(wct, true /* includingTopTask */); - prepareSplitLayout(wct); + prepareSplitLayout(wct, resizeAnim); } - private void prepareSplitLayout(WindowContainerTransaction wct) { - if (mIsDropEntering) { - mSplitLayout.resetDividerPosition(); - } else { + private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) { + if (resizeAnim) { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); + } else { + mSplitLayout.resetDividerPosition(); } updateWindowBounds(mSplitLayout, wct); - if (!mIsDropEntering) { + if (resizeAnim) { // Reset its smallest width dp to avoid is change layout before it actually resized to // split bounds. wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token, @@ -1523,21 +1528,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setRootForceTranslucent(false, wct); } - void finishEnterSplitScreen(SurfaceControl.Transaction t) { - mSplitLayout.update(t); + void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { + mSplitLayout.update(finishT); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash, getMainStageBounds()); mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash, getSideStageBounds()); - setDividerVisibility(true, t); + setDividerVisibility(true, finishT); // Ensure divider surface are re-parented back into the hierarchy at the end of the // transition. See Transition#buildFinishTransaction for more detail. - t.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); + finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - t.show(mRootTaskLeash); + updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); + finishT.show(mRootTaskLeash); setSplitsVisible(true); mIsDropEntering = false; + mSplitRequest = null; updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), @@ -1689,7 +1695,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mRootTaskInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, + mDisplayController, mDisplayImeController, mTaskOrganizer, PARALLAX_ALIGN_CENTER /* parallaxType */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -2153,8 +2159,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (displayId != DEFAULT_DISPLAY) { return; } - mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); - if (mSplitLayout != null && mSplitLayout.isDensityChanged(newConfig.densityDpi) && mMainStage.isActive() && mSplitLayout.updateConfiguration(newConfig) @@ -2171,10 +2175,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { - if (!mMainStage.isActive()) return; + if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return; - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); - mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); + mSplitLayout.rotateTo(toRotation); if (newDisplayAreaInfo != null) { mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration); } @@ -2500,6 +2503,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info, startTransaction, finishTransaction, finishCallback)) { mSplitLayout.update(startTransaction); + startTransaction.apply(); return true; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 92ff5fed4584..60628e62ec11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -183,12 +183,12 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); mChildrenTaskInfo.put(taskId, taskInfo); - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible); if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. return; } + updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); mCallbacks.onChildTaskAppeared(taskId); sendStatusChanged(); } else { @@ -417,7 +417,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } t.setCrop(leash, null); t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) { + if (firstAppeared) { t.setAlpha(leash, 1f); t.setMatrix(leash, 1, 0, 0, 1); t.show(leash); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 1bbd3679948b..163cf501734c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -61,6 +61,16 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { private TaskViewBase mTaskViewBase; private final Context mContext; + /** + * There could be a situation where we have task info and receive + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}, however, the + * activity might fail to open, and in this case we need to clean up the task view / notify + * listeners of a task removal. This requires task info, so we save the info from onTaskAppeared + * in this situation to allow us to notify listeners correctly if the task failed to open. + */ + private ActivityManager.RunningTaskInfo mPendingInfo; + /* Indicates that the task we attempted to launch in the task view failed to launch. */ + private boolean mTaskNotFound; protected ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; private SurfaceControl mTaskLeash; @@ -236,6 +246,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mTaskInfo = null; mTaskToken = null; mTaskLeash = null; + mPendingInfo = null; + mTaskNotFound = false; } private void updateTaskVisibility() { @@ -257,6 +269,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (isUsingShellTransitions()) { + mPendingInfo = taskInfo; + if (mTaskNotFound) { + // If we were already notified by shell transit that we don't have the + // the task, clean it up now. + cleanUpPendingTask(); + } // Everything else handled by enter transition. return; } @@ -455,6 +473,42 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { return mTaskInfo; } + /** + * Indicates that the task was not found in the start animation for the transition. + * In this case we should clean up the task if we have the pending info. If we don't + * have the pending info, we'll do it when we receive it in + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}. + */ + void setTaskNotFound() { + mTaskNotFound = true; + if (mPendingInfo != null) { + cleanUpPendingTask(); + } + } + + /** + * Called when a task failed to open and we need to clean up task view / + * notify users of task view. + */ + void cleanUpPendingTask() { + if (mPendingInfo != null) { + if (mListener != null) { + final int taskId = mPendingInfo.taskId; + mListenerExecutor.execute(() -> { + mListener.onTaskRemovalStarted(taskId); + }); + } + mTaskViewBase.onTaskVanished(mPendingInfo); + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mPendingInfo.token, false); + + // Make sure the task is removed + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.removeTask(mPendingInfo.token); + mTaskViewTransitions.closeTaskView(wct, this); + } + resetTaskInfo(); + } + void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) { if (mTaskToken == null) { // Nothing to update, task is not yet available @@ -492,6 +546,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { @NonNull SurfaceControl.Transaction finishTransaction, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash, WindowContainerTransaction wct) { + mPendingInfo = null; mTaskInfo = taskInfo; mTaskToken = mTaskInfo.token; mTaskLeash = leash; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 2e7fca3f2b46..5baf2e320227 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -139,7 +139,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { * `taskView`. * @param taskView the pending transition should be for this. */ - private PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { + @VisibleForTesting + PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { for (int i = mPending.size() - 1; i >= 0; --i) { if (mPending.get(i).mTaskView != taskView) continue; if (TransitionUtil.isOpeningType(mPending.get(i).mType)) { @@ -398,10 +399,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } } if (stillNeedsMatchingLaunch) { - throw new IllegalStateException("Expected a TaskView launch in this transition but" - + " didn't get one."); - } - if (wct == null && pending == null && changesHandled != info.getChanges().size()) { + Slog.w(TAG, "Expected a TaskView launch in this transition but didn't get one, " + + "cleaning up the task view"); + // Didn't find a task so the task must have never launched + pending.mTaskView.setTaskNotFound(); + } else if (wct == null && pending == null && changesHandled != info.getChanges().size()) { // Just some house-keeping, let another handler animate. return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 3ec433e55da9..b25437d95a99 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -30,6 +30,7 @@ import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.PendingIntent; import android.os.IBinder; import android.util.Log; import android.util.Pair; @@ -41,6 +42,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -590,6 +592,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return true; } + /** Use to when split use intent to enter, check if this enter transition should be mixed or + * not.*/ + public boolean shouldSplitEnterMixed(PendingIntent intent) { + // Check if this intent package is same as pip one or not, if true we want let the pip + // task enter split. + if (mPipHandler != null) { + return mPipHandler.isInPipPackage(SplitScreenUtils.getPackageName(intent.getIntent())); + } + return false; + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 3d779481d361..443cea245a4f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -41,6 +42,7 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; @@ -57,6 +59,7 @@ import org.mockito.MockitoAnnotations; public class SplitLayoutTests extends ShellTestCase { @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler; @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; + @Mock DisplayController mDisplayController; @Mock DisplayImeController mDisplayImeController; @Mock ShellTaskOrganizer mTaskOrganizer; @Mock WindowContainerTransaction mWct; @@ -72,6 +75,7 @@ public class SplitLayoutTests extends ShellTestCase { getConfiguration(), mSplitLayoutHandler, mCallbacks, + mDisplayController, mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_NONE)); @@ -100,6 +104,10 @@ public class SplitLayoutTests extends ShellTestCase { // Verify updateConfiguration returns true if the density changed. config.densityDpi = 123; assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration checks the current DisplayLayout + verify(mDisplayController, times(5)) // init * 1 + updateConfiguration * 4 + .getDisplayLayout(anyInt()); } @Test @@ -168,6 +176,14 @@ public class SplitLayoutTests extends ShellTestCase { verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt()); } + @Test + public void testRoateTo_checksDisplayLayout() { + mSplitLayout.rotateTo(90); + + verify(mDisplayController, times(2)) // init * 1 + rotateTo * 1 + .getDisplayLayout(anyInt()); + } + private void waitDividerFlingFinished() { verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(), mRunnableCaptor.capture()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 66b6c62f1dd6..2dcdc74e8ae7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -159,7 +159,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); verify(mMainStage).reparentTopTask(eq(wct)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); @@ -177,7 +177,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); } @@ -189,7 +189,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 81fc8438eec0..1b389565c066 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -128,8 +128,8 @@ public class TaskViewTest extends ShellTestCase { doReturn(true).when(mTransitions).isRegistered(); } mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions)); - mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer, - mTaskViewTransitions, mSyncQueue); + mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer, + mTaskViewTransitions, mSyncQueue)); mTaskView = new TaskView(mContext, mTaskViewTaskController); mTaskView.setListener(mExecutor, mViewListener); } @@ -544,4 +544,23 @@ public class TaskViewTest extends ShellTestCase { mTaskView.removeTask(); verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); } + + @Test + public void testOnTaskAppearedWithTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.setTaskNotFound(); + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + + verify(mTaskViewTaskController).cleanUpPendingTask(); + verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); + } + + @Test + public void testOnTaskAppeared_withoutTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + verify(mTaskViewTaskController, never()).cleanUpPendingTask(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 71ad0d79eaca..03ed18c86608 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -17,6 +17,7 @@ package com.android.wm.shell.taskview; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.google.common.truth.Truth.assertThat; @@ -25,16 +26,19 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.graphics.Rect; +import android.os.IBinder; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.transition.Transitions; @@ -45,6 +49,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.List; @SmallTest @@ -295,4 +300,34 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, new Rect(0, 0, 100, 100)); } + + @Test + public void test_startAnimation_setsTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + when(change.getTaskInfo()).thenReturn(mTaskInfo); + when(change.getMode()).thenReturn(TRANSIT_OPEN); + + List<TransitionInfo.Change> changes = new ArrayList<>(); + changes.add(change); + + TransitionInfo info = mock(TransitionInfo.class); + when(info.getChanges()).thenReturn(changes); + + mTaskViewTransitions.startTaskView(new WindowContainerTransaction(), + mTaskViewTaskController, + mock(IBinder.class)); + + TaskViewTransitions.PendingTransition pending = + mTaskViewTransitions.findPendingOpeningTransition(mTaskViewTaskController); + + mTaskViewTransitions.startAnimation(pending.mClaimed, + info, + new SurfaceControl.Transaction(), + new SurfaceControl.Transaction(), + mock(Transitions.TransitionFinishCallback.class)); + + verify(mTaskViewTaskController).setTaskNotFound(); + } } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt index 7e1bfb921ca9..addabcc0282a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt @@ -21,7 +21,6 @@ import android.graphics.fonts.FontVariationAxis import android.util.Log import android.util.LruCache import android.util.MathUtils -import android.util.MathUtils.abs import androidx.annotation.VisibleForTesting import java.lang.Float.max import java.lang.Float.min @@ -30,8 +29,6 @@ private const val TAG_WGHT = "wght" private const val TAG_ITAL = "ital" private const val FONT_WEIGHT_DEFAULT_VALUE = 400f -private const val FONT_WEIGHT_ANIMATION_FRAME_COUNT = 100 - private const val FONT_ITALIC_MAX = 1f private const val FONT_ITALIC_MIN = 0f private const val FONT_ITALIC_ANIMATION_STEP = 0.1f @@ -39,11 +36,12 @@ private const val FONT_ITALIC_DEFAULT_VALUE = 0f // Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in // frame draw time on a Pixel 6. -@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10 +@VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10 /** Provide interpolation of two fonts by adjusting font variation settings. */ -class FontInterpolator { - +class FontInterpolator( + numberOfAnimationSteps: Int? = null, +) { /** * Cache key for the interpolated font. * @@ -88,8 +86,9 @@ class FontInterpolator { // Font interpolator has two level caches: one for input and one for font with different // variation settings. No synchronization is needed since FontInterpolator is not designed to be // thread-safe and can be used only on UI thread. - private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES) - private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES) + val cacheMaxEntries = numberOfAnimationSteps?.let { it * 2 } ?: DEFAULT_FONT_CACHE_MAX_ENTRIES + private val interpCache = LruCache<InterpKey, Font>(cacheMaxEntries) + private val verFontCache = LruCache<VarFontKey, Font>(cacheMaxEntries) // Mutable keys for recycling. private val tmpInterpKey = InterpKey(null, null, 0f) @@ -128,18 +127,12 @@ class FontInterpolator { val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> when (tag) { - // TODO: Good to parse 'fvar' table for retrieving default value. - TAG_WGHT -> { - adaptiveAdjustWeight( - MathUtils.lerp( - startValue ?: FONT_WEIGHT_DEFAULT_VALUE, - endValue ?: FONT_WEIGHT_DEFAULT_VALUE, - progress - ), + TAG_WGHT -> + MathUtils.lerp( startValue ?: FONT_WEIGHT_DEFAULT_VALUE, endValue ?: FONT_WEIGHT_DEFAULT_VALUE, + progress ) - } TAG_ITAL -> adjustItalic( MathUtils.lerp( @@ -175,9 +168,9 @@ class FontInterpolator { val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build() interpCache.put(InterpKey(start, end, progress), newFont) verFontCache.put(VarFontKey(start, newAxes), newFont) - if (DEBUG) { - Log.d(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey") - } + + // Cache misses are likely to create memory leaks, so this is logged at error level. + Log.e(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey") return newFont } @@ -225,15 +218,6 @@ class FontInterpolator { return result } - // For the performance reasons, we animate weight with adaptive step. This helps - // Cache hit ratio in the Skia glyph cache. - // The reason we don't use fix step is because the range of weight axis is not normalized, - // some are from 50 to 100, others are from 0 to 1000, so we cannot give a constant proper step - private fun adaptiveAdjustWeight(value: Float, start: Float, end: Float): Float { - val step = max(abs(end - start) / FONT_WEIGHT_ANIMATION_FRAME_COUNT, 1F) - return coerceInWithStep(value, min(start, end), max(start, end), step) - } - // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps // Cache hit ratio in the Skia glyph cache. private fun adjustItalic(value: Float) = diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 16ddf0c36d9d..b555fa583588 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -108,7 +108,8 @@ class TextAnimator( } // Following two members are for mutable for testing purposes. - public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache) + public var textInterpolator: TextInterpolator = + TextInterpolator(layout, typefaceCache, numberOfAnimationSteps) public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 8ed8d8fb61fd..02caeeddd774 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -31,6 +31,7 @@ import java.lang.Math.max class TextInterpolator( layout: Layout, var typefaceCache: TypefaceVariantCache, + numberOfAnimationSteps: Int? = null, ) { /** * Returns base paint used for interpolation. @@ -85,7 +86,7 @@ class TextInterpolator( private class Line(val runs: List<Run>) private var lines = listOf<Line>() - private val fontInterpolator = FontInterpolator() + private val fontInterpolator = FontInterpolator(numberOfAnimationSteps) // Recycling object for glyph drawing and tweaking. private val tmpPaint = TextPaint() diff --git a/packages/SystemUI/plugin_core/Android.bp b/packages/SystemUI/plugin_core/Android.bp index 34d31d9955fc..4e39f1ac9545 100644 --- a/packages/SystemUI/plugin_core/Android.bp +++ b/packages/SystemUI/plugin_core/Android.bp @@ -25,6 +25,9 @@ java_library { sdk_version: "current", name: "PluginCoreLib", srcs: ["src/**/*.java"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, // Enforce that the library is built against java 8 so that there are // no compatibility issues with launcher diff --git a/packages/SystemUI/plugin_core/proguard.flags b/packages/SystemUI/plugin_core/proguard.flags new file mode 100644 index 000000000000..6240898b3b93 --- /dev/null +++ b/packages/SystemUI/plugin_core/proguard.flags @@ -0,0 +1,11 @@ +# R8's full mode is a bit more aggressive in stripping annotations, but the +# SystemUI plugin architecture requires these annotations at runtime. The +# following rules are the minimal set necessary to ensure compatibility. +# For more details, see: +# https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md#r8-full-mode +-keepattributes RuntimeVisible*Annotation*,AnnotationDefault + +-keep interface com.android.systemui.plugins.annotations.** { + *; +} +-keep,allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification @com.android.systemui.plugins.annotations.** class * diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3fc0965f4a81..fc9c917c152b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -43,7 +43,7 @@ <!-- Not quite optimal but needed to translate these items as a group. The NotificationIconContainer has its own logic for translation. --> - <LinearLayout + <com.android.keyguard.KeyguardStatusAreaView android:id="@+id/keyguard_status_area" android:orientation="vertical" android:layout_width="match_parent" @@ -63,5 +63,5 @@ android:paddingStart="@dimen/below_clock_padding_start_icons" android:visibility="invisible" /> - </LinearLayout> + </com.android.keyguard.KeyguardStatusAreaView> </com.android.keyguard.KeyguardClockSwitch> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml index 2a8092010a37..3d1cb5ee6177 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml +++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml @@ -16,5 +16,5 @@ --> <resources> <!-- Invisibility to use for the date & weather view when it is disabled by a clock --> - <integer name="keyguard_date_weather_view_invisibility">8</integer> + <integer name="keyguard_date_weather_view_invisibility">4</integer> </resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 4b7968953420..39dd90e5ed60 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -148,4 +148,9 @@ <dimen name="default_dot_diameter">34dp</dimen> <dimen name="default_dot_spacing">0dp</dimen> + <!-- Weather clock smartspace scaling to apply for the weather clock --> + <item name="weather_clock_smartspace_scale" type="dimen" format="float">1.0</item> + <dimen name="weather_clock_smartspace_translateX">0dp</dimen> + <dimen name="weather_clock_smartspace_translateY">0dp</dimen> + </resources> diff --git a/packages/SystemUI/res-keyguard/values/ids.xml b/packages/SystemUI/res-keyguard/values/ids.xml index 0dff4ffa3866..1435907eaef6 100644 --- a/packages/SystemUI/res-keyguard/values/ids.xml +++ b/packages/SystemUI/res-keyguard/values/ids.xml @@ -17,4 +17,18 @@ <resources> <item type="id" name="header_footer_views_added_tag_key" /> + + <!-- animation channels for keyguard status area --> + <item type="id" name="translate_x_clock_design_animator_tag" /> + <item type="id" name="translate_x_clock_design_animator_start_tag" /> + <item type="id" name="translate_x_clock_design_animator_end_tag" /> + <item type="id" name="translate_x_aod_animator_tag" /> + <item type="id" name="translate_x_aod_animator_start_tag" /> + <item type="id" name="translate_x_aod_animator_end_tag" /> + <item type="id" name="translate_y_clock_size_animator_tag" /> + <item type="id" name="translate_y_clock_size_animator_start_tag" /> + <item type="id" name="translate_y_clock_size_animator_end_tag" /> + <item type="id" name="translate_y_clock_design_animator_tag" /> + <item type="id" name="translate_y_clock_design_animator_start_tag" /> + <item type="id" name="translate_y_clock_design_animator_end_tag" /> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index c5979cc50c3c..7a8c82cee32a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -65,8 +65,8 @@ class UnfoldConstantTranslateAnimator( } else { 1 } - viewsToTranslate.forEach { (view, direction) -> - view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier + viewsToTranslate.forEach { (view, direction, func) -> + view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) } } } @@ -77,7 +77,7 @@ class UnfoldConstantTranslateAnimator( .filter { it.shouldBeAnimated() } .mapNotNull { parent.findViewById<View>(it.viewId)?.let { view -> - ViewToTranslate(WeakReference(view), it.direction) + ViewToTranslate(WeakReference(view), it.direction, it.translateFunc) } } .toList() @@ -91,14 +91,19 @@ class UnfoldConstantTranslateAnimator( data class ViewIdToTranslate( val viewId: Int, val direction: Direction, - val shouldBeAnimated: () -> Boolean = { true } + val shouldBeAnimated: () -> Boolean = { true }, + val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value }, ) /** * Represents a view whose animation process is in-progress. It should be immutable because the * started animation should be completed. */ - private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction) + private data class ViewToTranslate( + val view: WeakReference<View>, + val direction: Direction, + val translateFunc: (View, Float) -> Unit, + ) /** Direction of the animation. */ enum class Direction(val multiplier: Float) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index d9d64ad5a893..376e27c6cf44 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,8 +1,14 @@ package com.android.keyguard; import static android.view.View.ALPHA; +import static android.view.View.SCALE_X; +import static android.view.View.SCALE_Y; import static android.view.View.TRANSLATION_Y; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -17,6 +23,7 @@ import android.widget.RelativeLayout; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; +import androidx.core.content.res.ResourcesCompat; import com.android.app.animation.Interpolators; import com.android.keyguard.dagger.KeyguardStatusViewScope; @@ -44,6 +51,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final long STATUS_AREA_START_DELAY_MILLIS = 0; private static final long STATUS_AREA_MOVE_UP_MILLIS = 967; private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467; + private static final float SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER = 1.4f; @IntDef({LARGE, SMALL}) @Retention(RetentionPolicy.SOURCE) @@ -88,14 +96,18 @@ public class KeyguardClockSwitch extends RelativeLayout { private KeyguardClockFrame mLargeClockFrame; private ClockController mClock; - private View mStatusArea; + private KeyguardStatusAreaView mStatusArea; private int mSmartspaceTopOffset; + private float mWeatherClockSmartspaceScaling = 1f; + private int mWeatherClockSmartspaceTranslateX = 0; + private int mWeatherClockSmartspaceTranslateY = 0; private int mDrawAlpha = 255; /** * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + private boolean mSplitShadeCentered = false; /** * Indicates which clock is currently displayed - should be one of {@link ClockSize}. @@ -105,7 +117,7 @@ public class KeyguardClockSwitch extends RelativeLayout { @VisibleForTesting AnimatorSet mClockInAnim = null; @VisibleForTesting AnimatorSet mClockOutAnim = null; - private AnimatorSet mStatusAreaAnim = null; + @VisibleForTesting AnimatorSet mStatusAreaAnim = null; private int mClockSwitchYAmount; @VisibleForTesting boolean mChildrenAreLaidOut = false; @@ -117,13 +129,30 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { + public void onConfigChanged() { mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_clock_switch_y_shift); mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset); + mWeatherClockSmartspaceScaling = ResourcesCompat.getFloat( + mContext.getResources(), R.dimen.weather_clock_smartspace_scale); + mWeatherClockSmartspaceTranslateX = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateX); + mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateY); + updateStatusArea(/* animate= */false); + } + + /** + * Enable or disable split shade specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + if (mSplitShadeCentered != splitShadeCentered) { + mSplitShadeCentered = splitShadeCentered; + updateStatusArea(/* animate= */true); + } } @Override @@ -134,7 +163,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large); mStatusArea = findViewById(R.id.keyguard_status_area); - onDensityOrFontScaleChanged(); + onConfigChanged(); } @Override @@ -182,6 +211,13 @@ public class KeyguardClockSwitch extends RelativeLayout { mSmallClockFrame.addView(clock.getSmallClock().getView()); mLargeClockFrame.addView(clock.getLargeClock().getView()); updateClockTargetRegions(); + updateStatusArea(/* animate= */false); + } + + private void updateStatusArea(boolean animate) { + if (mDisplayedClockSize != null && mChildrenAreLaidOut) { + updateClockViews(mDisplayedClockSize == LARGE, animate); + } } void updateClockTargetRegions() { @@ -230,13 +266,25 @@ public class KeyguardClockSwitch extends RelativeLayout { mStatusAreaAnim = null; View in, out; - float statusAreaYTranslation, clockInYTranslation, clockOutYTranslation; + float statusAreaYTranslation, statusAreaClockScale = 1f; + float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f; + float clockInYTranslation, clockOutYTranslation; if (useLargeClock) { out = mSmallClockFrame; in = mLargeClockFrame; if (indexOfChild(in) == -1) addView(in, 0); statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop() + mSmartspaceTopOffset; + // TODO: Load from clock config when less risky + if (mClock != null + && mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) { + statusAreaClockScale = mWeatherClockSmartspaceScaling; + statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX; + statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY; + if (mSplitShadeCentered) { + statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER; + } + } clockInYTranslation = 0; clockOutYTranslation = 0; // Small clock translation is handled with statusArea } else { @@ -258,7 +306,12 @@ public class KeyguardClockSwitch extends RelativeLayout { in.setAlpha(1f); in.setTranslationY(clockInYTranslation); in.setVisibility(View.VISIBLE); - mStatusArea.setTranslationY(statusAreaYTranslation); + mStatusArea.setScaleX(statusAreaClockScale); + mStatusArea.setScaleY(statusAreaClockScale); + mStatusArea.setTranslateXFromClockDesign(statusAreaClockTranslateX); + mStatusArea.setTranslateYFromClockDesign(statusAreaClockTranslateY); + mStatusArea.setTranslateYFromClockSize(statusAreaYTranslation); + mSmallClockFrame.setTranslationY(statusAreaYTranslation); return; } @@ -295,8 +348,15 @@ public class KeyguardClockSwitch extends RelativeLayout { useLargeClock ? STATUS_AREA_MOVE_UP_MILLIS : STATUS_AREA_MOVE_DOWN_MILLIS); mStatusAreaAnim.setInterpolator(Interpolators.EMPHASIZED); mStatusAreaAnim.playTogether( - ObjectAnimator.ofFloat(mStatusArea, TRANSLATION_Y, statusAreaYTranslation), - ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation)); + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_SIZE.getProperty(), + statusAreaYTranslation), + ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation), + ObjectAnimator.ofFloat(mStatusArea, SCALE_X, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, SCALE_Y, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_X_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateX), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateY)); mStatusAreaAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { mStatusAreaAnim = null; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 99e25745dda7..41c1eda42e83 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -330,10 +330,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { - mView.onDensityOrFontScaleChanged(); + public void onConfigChanged() { + mView.onConfigChanged(); mKeyguardSmallClockTopMargin = mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); mKeyguardLargeClockTopMargin = @@ -344,6 +344,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS setDateWeatherVisibility(); } + /** + * Enable or disable split shade center specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + mView.setSplitShadeCentered(splitShadeCentered); + } /** * Set which clock should be displayed on the keyguard. The other one will be automatically @@ -407,7 +413,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS scale, props, animate); if (mStatusArea != null) { - PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X, + PropertyAnimator.setProperty(mStatusArea, KeyguardStatusAreaView.TRANSLATE_X_AOD, x, props, animate); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt new file mode 100644 index 000000000000..e7da2b977379 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt @@ -0,0 +1,118 @@ +package com.android.keyguard + +import android.content.Context +import android.util.AttributeSet +import android.util.FloatProperty +import android.widget.LinearLayout +import com.android.systemui.R +import com.android.systemui.statusbar.notification.AnimatableProperty + +class KeyguardStatusAreaView( + context: Context, + attrs: AttributeSet? = null, +) : LinearLayout(context, attrs) { + var translateXFromClockDesign = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromAod = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromUnfold = 0F + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateYFromClockSize = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockDesign + } + + var translateYFromClockDesign = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockSize + } + + companion object { + @JvmField + val TRANSLATE_X_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromClockDesign + } + }, + R.id.translate_x_clock_design_animator_tag, + R.id.translate_x_clock_design_animator_start_tag, + R.id.translate_x_clock_design_animator_end_tag + ) + + @JvmField + val TRANSLATE_X_AOD = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXAod") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromAod = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromAod + } + }, + R.id.translate_x_aod_animator_tag, + R.id.translate_x_aod_animator_start_tag, + R.id.translate_x_aod_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_SIZE = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockSize") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockSize = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockSize + } + }, + R.id.translate_y_clock_size_animator_tag, + R.id.translate_y_clock_size_animator_start_tag, + R.id.translate_y_clock_size_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockDesign + } + }, + R.id.translate_y_clock_design_animator_tag, + R.id.translate_y_clock_design_animator_start_tag, + R.id.translate_y_clock_design_animator_end_tag + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 835cc13bebb1..00500d617766 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.content.res.Configuration; import android.graphics.Rect; import android.transition.ChangeBounds; import android.transition.Transition; @@ -280,8 +281,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } @Override - public void onDensityOrFontScaleChanged() { - mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); + public void onConfigChanged(Configuration newConfig) { + mKeyguardClockSwitchController.onConfigChanged(); } }; @@ -329,6 +330,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { + mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered); if (mStatusViewCentered == shouldBeCentered) { return; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index edd150c293f6..ca64ae059093 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -53,7 +53,10 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard), + ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, + { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + }), ViewIdToTranslate( R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8137dec4851d..c7e817e5781a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3670,7 +3670,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.logSimState(subId, slotId, state); boolean becameAbsent = false; - if (!SubscriptionManager.isValidSubscriptionId(subId)) { + if (!SubscriptionManager.isValidSubscriptionId(subId) + && state != TelephonyManager.SIM_STATE_UNKNOWN) { mLogger.w("invalid subId in handleSimStateChange()"); /* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to * handleServiceStateChange() handle other case */ @@ -3705,7 +3706,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab data.subId = subId; data.slotId = slotId; } - if ((changed || becameAbsent) && state != TelephonyManager.SIM_STATE_UNKNOWN) { + if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java index 4557b3418e19..500910c910c3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java @@ -30,6 +30,7 @@ import android.transition.TransitionManager; import android.transition.TransitionValues; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -51,12 +52,30 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu private int mPosition = 0; private final PinShapeAdapter mPinShapeAdapter; private ValueAnimator mValueAnimator = ValueAnimator.ofFloat(1f, 0f); + private Rect mFirstChildVisibleRect = new Rect(); public PinShapeNonHintingView(Context context, AttributeSet attrs) { super(context, attrs); mPinShapeAdapter = new PinShapeAdapter(context); } @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (getChildCount() > 0) { + View firstChild = getChildAt(0); + boolean isVisible = firstChild.getLocalVisibleRect(mFirstChildVisibleRect); + boolean clipped = mFirstChildVisibleRect.left > 0 + || mFirstChildVisibleRect.right < firstChild.getWidth(); + if (!isVisible || clipped) { + setGravity(Gravity.END | Gravity.CENTER_VERTICAL); + return; + } + } + + setGravity(Gravity.CENTER); + } + + @Override public void append() { int size = getResources().getDimensionPixelSize(R.dimen.password_shape_size); ImageView pinDot = new ImageView(getContext()); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 6948c8d4e563..0e0cf74ba463 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -697,6 +697,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } break; + case TelephonyManager.SIM_STATE_UNKNOWN: + mPendingPinLock = false; + break; default: if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState); break; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index e52418912e1f..8b3d7a653232 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -15,8 +15,6 @@ */ package com.android.systemui.navigationbar.gestural; -import static android.view.InputDevice.SOURCE_MOUSE; -import static android.view.InputDevice.SOURCE_TOUCHPAD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.classifier.Classifier.BACK_GESTURE; @@ -951,12 +949,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mMLResults = 0; mLogGesture = false; mInRejectedExclusion = false; - // Trackpad back gestures don't have zones, so we don't need to check if the down event - // is within insets. Also we don't allow back for button press from the trackpad, and - // yet we do with a mouse. boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); + // Trackpad back gestures don't have zones, so we don't need to check if the down event + // is within insets. mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed - && !isButtonPressFromTrackpad(ev) && (isTrackpadMultiFingerSwipe || isWithinInsets) && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) @@ -1069,11 +1065,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.scheduleFrameUpdate(); } - private boolean isButtonPressFromTrackpad(MotionEvent ev) { - int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); - return (sources & (SOURCE_MOUSE | SOURCE_TOUCHPAD)) == sources && ev.getButtonState() != 0; - } - private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { mVelocityTracker.addMovement(event); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index b848d2e84faf..f37a9b5842a7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -390,8 +390,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, public void init(int windowType, Callback callback) { initDialog(mActivityManager.getLockTaskModeState()); - mAccessibility.init(); - mController.addCallback(mControllerCallbackH, mHandler); mController.getState(); @@ -478,8 +476,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); @@ -677,6 +674,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, initRingerH(); initSettingsH(lockTaskModeState); initODICaptionsH(); + mAccessibility.init(); } private boolean isWindowGravityLeft() { @@ -930,6 +928,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, showRingerDrawer(); } }); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mRingerDrawerVibrate.setOnClickListener( new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE)); @@ -992,6 +991,19 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, : 0; } + @VisibleForTesting String getSelectedRingerContainerDescription() { + return mSelectedRingerContainer == null ? null : + mSelectedRingerContainer.getContentDescription().toString(); + } + + @VisibleForTesting void toggleRingerDrawer(boolean show) { + if (show) { + showRingerDrawer(); + } else { + hideRingerDrawer(); + } + } + /** Animates in the ringer drawer. */ private void showRingerDrawer() { if (mIsRingerDrawerOpen) { @@ -1069,12 +1081,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .start(); } - // When the ringer drawer is open, tapping the currently selected ringer will set the ringer - // to the current ringer mode. Change the content description to that, instead of the 'tap - // to change ringer mode' default. - mSelectedRingerContainer.setContentDescription( - mContext.getString(getStringDescriptionResourceForRingerMode( - mState.ringerModeInternal))); + updateSelectedRingerContainerDescription(true); mIsRingerDrawerOpen = true; } @@ -1120,14 +1127,38 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .translationY(0f) .start(); - // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the - // user to change the ringer. - mSelectedRingerContainer.setContentDescription( - mContext.getString(R.string.volume_ringer_change)); + updateSelectedRingerContainerDescription(false); mIsRingerDrawerOpen = false; } + + /** + * @param open false to set the description when drawer is closed + */ + private void updateSelectedRingerContainerDescription(boolean open) { + if (mState == null || mSelectedRingerContainer == null) return; + + String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode( + mState.ringerModeInternal)); + String tapToSelect; + + if (open) { + // When the ringer drawer is open, tapping the currently selected ringer will set the + // ringer to the current ringer mode. Change the content description to that, instead of + // the 'tap to change ringer mode' default. + tapToSelect = ""; + + } else { + // When the drawer is closed, tapping the selected ringer drawer will open it, allowing + // the user to change the ringer. The user needs to know that, and also the current mode + currentMode += ", "; + tapToSelect = mContext.getString(R.string.volume_ringer_change); + } + + mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect); + } + private void initSettingsH(int lockTaskModeState) { if (mSettingsView != null) { mSettingsView.setVisibility( @@ -1703,7 +1734,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, }); } - private int getStringDescriptionResourceForRingerMode(int mode) { + @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) { switch (mode) { case RINGER_MODE_SILENT: return R.string.volume_ringer_status_silent; @@ -1785,6 +1816,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, updateVolumeRowH(row); } updateRingerH(); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mWindow.setTitle(composeWindowTitle()); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 254f9531ef83..061340e385a5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -72,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { private FrameLayout mSmallClockFrame; private FrameLayout mLargeClockFrame; + private KeyguardStatusAreaView mStatusArea; KeyguardClockSwitch mKeyguardClockSwitch; @@ -109,6 +110,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); + mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); mKeyguardClockSwitch.mChildrenAreLaidOut = true; } @@ -185,6 +187,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -206,6 +209,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -226,6 +230,31 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test + public void switchingToSmallClockAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, true); + + mKeyguardClockSwitch.mClockInAnim.end(); + mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + @Test + public void switchingToSmallClockNoAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, false); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + + @Test public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue(); assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt new file mode 100644 index 000000000000..e6b696454d42 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt @@ -0,0 +1,44 @@ +package com.android.keyguard + +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class KeyguardStatusAreaViewTest : SysuiTestCase() { + + private lateinit var view: KeyguardStatusAreaView + + @Before + fun setUp() { + view = KeyguardStatusAreaView(context) + } + + @Test + fun checkTranslationX_AddedTotals() { + view.translateXFromClockDesign = 10f + assertEquals(10f, view.translationX) + + view.translateXFromAod = 20f + assertEquals(30f, view.translationX) + + view.translateXFromUnfold = 30f + assertEquals(60f, view.translationX) + } + + @Test + fun checkTranslationY_AddedTotals() { + view.translateYFromClockSize = 10f + assertEquals(10f, view.translationY) + + view.translateYFromClockDesign = 20f + assertEquals(30f, view.translationY) + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 419d045885a8..de306d669476 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -2800,6 +2800,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { ); } + @Test + public void testOnSimStateChanged_Unknown() { + KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy( + KeyguardUpdateMonitorCallback.class); + mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback); + mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_UNKNOWN); + verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0, + TelephonyManager.SIM_STATE_UNKNOWN); + } + private void verifyFingerprintAuthenticateNeverCalled() { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt index 57a355f4e127..5e1a8e1432dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt @@ -37,7 +37,7 @@ class FontInterpolatorTest : SysuiTestCase() { private fun assertSameAxes(expect: Font, actual: Font) { val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } private fun assertSameAxes(expectVarSettings: String, actual: Font) { @@ -46,7 +46,7 @@ class FontInterpolatorTest : SysuiTestCase() { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } @Test @@ -61,7 +61,7 @@ class FontInterpolatorTest : SysuiTestCase() { val interp = FontInterpolator() assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f)) - assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -74,7 +74,7 @@ class FontInterpolatorTest : SysuiTestCase() { .build() val interp = FontInterpolator() - assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -118,7 +118,7 @@ class FontInterpolatorTest : SysuiTestCase() { .setFontVariationSettings("'wght' 1") .build() val resultFont = interp.lerp(startFont, endFont, 0.5f) - for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) { + for (i in 0..interp.cacheMaxEntries + 1) { val f1 = Font.Builder(sFont) .setFontVariationSettings("'wght' ${i * 100}") .build() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS index adb10f01b5e1..5420c377be39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS @@ -1,4 +1,5 @@ set noparent +# Bug component: 879035 include /services/core/java/com/android/server/biometrics/OWNERS beverlyt@google.com diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..ef3a33248597 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -16,13 +16,19 @@ package com.android.systemui.volume; +import static android.media.AudioManager.RINGER_MODE_NORMAL; +import static android.media.AudioManager.RINGER_MODE_SILENT; +import static android.media.AudioManager.RINGER_MODE_VIBRATE; + import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -293,7 +299,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectVibrateFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -307,7 +313,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -329,7 +335,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { // Make sure we've actually changed the ringer mode. verify(mVolumeDialogController, times(1)).setRingerMode( - AudioManager.RINGER_MODE_NORMAL, false); + RINGER_MODE_NORMAL, false); } /** @@ -511,6 +517,87 @@ public class VolumeDialogImplTest extends SysuiTestCase { } } + private enum RingerDrawerState {INIT, OPEN, CLOSE} + + @Test + public void ringerModeNormal_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT); + } + + @Test + public void ringerModeSilent_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT); + } + + @Test + public void ringerModeVibrate_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT); + } + + @Test + public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); + } + + /** + * The content description should include ringer state, and the correct one. + */ + private void assertRingerContainerDescribesItsState(int ringerMode, + RingerDrawerState drawerState) { + State state = createShellState(); + state.ringerModeInternal = ringerMode; + mDialog.onStateChangedH(state); + + mDialog.show(SHOW_REASON_UNKNOWN); + + if (drawerState != RingerDrawerState.INIT) { + // in both cases we first open the drawer + mDialog.toggleRingerDrawer(true); + + if (drawerState == RingerDrawerState.CLOSE) { + mDialog.toggleRingerDrawer(false); + } + } + + String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription(); + assumeNotNull(ringerContainerDescription); + + String ringerDescription = mContext.getString( + mDialog.getStringDescriptionResourceForRingerMode(ringerMode)); + + if (drawerState == RingerDrawerState.OPEN) { + assertEquals(ringerDescription, ringerContainerDescription); + } else { + assertNotSame(ringerDescription, ringerContainerDescription); + assertTrue(ringerContainerDescription.startsWith(ringerDescription)); + } + } + @After public void teardown() { if (mDialog != null) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1a57bc1b7a0d..ec4203e3390c 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -63,6 +63,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArrayMap; import android.util.LocalLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -334,7 +335,8 @@ public final class AutofillManagerService // of time. synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.onSwitchInputMethod(); } @@ -366,11 +368,12 @@ public final class AutofillManagerService boolean isTemporary) { mAugmentedAutofillState.setServiceInfo(userId, serviceName, isTemporary); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteAugmentedAutofillService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteAugmentedAutofillService(); } @@ -380,17 +383,46 @@ public final class AutofillManagerService private void onFieldClassificationServiceNameChanged( @UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteFieldClassificationService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteFieldClassificationService(); } } } + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl getServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = getServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl peekServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = peekServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { @@ -1038,7 +1070,8 @@ public final class AutofillManagerService mUi.hideAll(null); synchronized (mLock) { final AutofillManagerServiceImpl service = - getServiceForUserLocked(UserHandle.getCallingUserId()); + getServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); service.onBackKeyPressed(); } } @@ -1537,20 +1570,27 @@ public final class AutofillManagerService public void addClient(IAutoFillManagerClient client, ComponentName componentName, int userId, IResultReceiver receiver) { int flags = 0; - synchronized (mLock) { - final int enabledFlags = getServiceForUserLocked(userId).addClientLocked(client, - componentName); - if (enabledFlags != 0) { - flags |= enabledFlags; - } - if (sDebug) { - flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; - } - if (sVerbose) { - flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + try { + synchronized (mLock) { + final int enabledFlags = + getServiceForUserWithLocalBinderIdentityLocked(userId) + .addClientLocked(client, componentName); + if (enabledFlags != 0) { + flags |= enabledFlags; + } + if (sDebug) { + flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } + if (sVerbose) { + flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + } } + } catch (Exception ex) { + // Don't do anything, send back default flags + Log.wtf(TAG, "addClient(): failed " + ex.toString()); + } finally { + send(receiver, flags); } - send(receiver, flags); } @Override @@ -1569,7 +1609,8 @@ public final class AutofillManagerService public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setAuthenticationResultLocked(data, sessionId, authenticationId, getCallingUid()); } @@ -1578,7 +1619,8 @@ public final class AutofillManagerService @Override public void setHasCallback(int sessionId, int userId, boolean hasIt) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setHasCallback(sessionId, getCallingUid(), hasIt); } } @@ -1608,7 +1650,8 @@ public final class AutofillManagerService final int taskId = mAm.getTaskIdForActivity(activityToken, false); final long result; synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); result = service.startSessionLocked(activityToken, taskId, getCallingUid(), clientCallback, autofillId, bounds, value, hasCallback, clientActivity, compatMode, mAllowInstantService, flags); @@ -1624,51 +1667,72 @@ public final class AutofillManagerService @Override public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException { + FillEventHistory fillEventHistory = null; final int userId = UserHandle.getCallingUserId(); - FillEventHistory fillEventHistory = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - fillEventHistory = service.getFillEventHistory(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + fillEventHistory = service.getFillEventHistory(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getFillEventHistory(): failed " + ex.toString()); + } finally { + send(receiver, fillEventHistory); } - send(receiver, fillEventHistory); } @Override public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException { + UserData userData = null; final int userId = UserHandle.getCallingUserId(); - UserData userData = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserData(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserData(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserData(): failed " + ex.toString()); + } finally { + send(receiver, userData); } - send(receiver, userData); } @Override public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); UserData userData = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserDataId(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserDataId(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserDataId(): failed " + ex.toString()); + } finally { + final String userDataId = userData == null ? null : userData.getId(); + send(receiver, userDataId); } - final String userDataId = userData == null ? null : userData.getId(); - send(receiver, userDataId); } @Override @@ -1676,7 +1740,8 @@ public final class AutofillManagerService final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setUserData(getCallingUid(), userData); } else if (sVerbose) { @@ -1688,124 +1753,171 @@ public final class AutofillManagerService @Override public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); boolean enabled = false; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - enabled = service.isFieldClassificationEnabled(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + enabled = service.isFieldClassificationEnabled(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back false + Log.wtf(TAG, "isFieldClassificationEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String algorithm = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + } } - } + } + } catch (Exception ex) { + // Do not raise the exception, just send back null + Log.wtf(TAG, "getDefaultFieldClassificationAlgorithm(): failed " + ex.toString()); + } finally { + send(receiver, algorithm); } - send(receiver, algorithm); + } @Override public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, @Nullable List<ComponentName> activities, @NonNull IResultReceiver receiver) throws RemoteException { + boolean ok = false; final int userId = UserHandle.getCallingUserId(); - boolean ok; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, - getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, + getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + + userId); + } } - ok = false; } + } catch (Exception ex) { + // Do not raise the exception, return the default value + Log.wtf(TAG, "setAugmentedAutofillWhitelist(): failed " + ex.toString()); + } finally { + send(receiver, + ok ? AutofillManager.RESULT_OK + : AutofillManager.RESULT_CODE_NOT_SERVICE); } - send(receiver, - ok ? AutofillManager.RESULT_OK : AutofillManager.RESULT_CODE_NOT_SERVICE); } @Override public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String[] algorithms = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithms = service + .getAvailableFieldClassificationAlgorithms(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + } } } + } catch (Exception ex) { + // Do not raise the exception, return null + Log.wtf(TAG, "getAvailableFieldClassificationAlgorithms(): failed " + + ex.toString()); + } finally { + send(receiver, algorithms); } - send(receiver, algorithms); } @Override public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver) throws RemoteException { + ComponentName componentName = null; final int userId = UserHandle.getCallingUserId(); - ComponentName componentName = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - componentName = service.getServiceComponentName(); - } else if (sVerbose) { - Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + componentName = service.getServiceComponentName(); + } else if (sVerbose) { + Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + } } + } catch (Exception ex) { + Log.wtf(TAG, "getAutofillServiceComponentName(): failed " + ex.toString()); + } finally { + send(receiver, componentName); } - send(receiver, componentName); } @Override public void restoreSession(int sessionId, @NonNull IBinder activityToken, @NonNull IBinder appCallback, @NonNull IResultReceiver receiver) throws RemoteException { + boolean restored = false; final int userId = UserHandle.getCallingUserId(); - Objects.requireNonNull(activityToken, "activityToken"); - Objects.requireNonNull(appCallback, "appCallback"); - boolean restored = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - restored = service.restoreSession(sessionId, getCallingUid(), activityToken, - appCallback); - } else if (sVerbose) { - Slog.v(TAG, "restoreSession(): no service for " + userId); + try { + Objects.requireNonNull(activityToken, "activityToken"); + Objects.requireNonNull(appCallback, "appCallback"); + + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + restored = service.restoreSession(sessionId, getCallingUid(), activityToken, + appCallback); + } else if (sVerbose) { + Slog.v(TAG, "restoreSession(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not propagate exception, send back status + Log.wtf(TAG, "restoreSession(): failed " + ex.toString()); + } finally { + send(receiver, restored); } - send(receiver, restored); } @Override public void updateSession(int sessionId, AutofillId autoFillId, Rect bounds, AutofillValue value, int action, int flags, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds, value, action, flags); @@ -1818,7 +1930,8 @@ public final class AutofillManagerService @Override public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setAutofillFailureLocked(sessionId, getCallingUid(), ids); } else if (sVerbose) { @@ -1831,7 +1944,8 @@ public final class AutofillManagerService public void finishSession(int sessionId, int userId, @AutofillCommitReason int commitReason) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.finishSessionLocked(sessionId, getCallingUid(), commitReason); } else if (sVerbose) { @@ -1843,19 +1957,22 @@ public final class AutofillManagerService @Override public void cancelSession(int sessionId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.cancelSessionLocked(sessionId, getCallingUid()); } else if (sVerbose) { Slog.v(TAG, "cancelSession(): no service for " + userId); } } + } @Override public void disableOwnedAutofillServices(int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.disableOwnedAutofillServicesLocked(Binder.getCallingUid()); } else if (sVerbose) { @@ -1867,21 +1984,36 @@ public final class AutofillManagerService @Override public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) { boolean supported = false; - synchronized (mLock) { - supported = !isDisabledLocked(userId); + + try { + synchronized (mLock) { + supported = !isDisabledLocked(userId); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceSupported(): failed " + ex.toString()); + } finally { + send(receiver, supported); } - send(receiver, supported); } @Override public void isServiceEnabled(int userId, @NonNull String packageName, @NonNull IResultReceiver receiver) { boolean enabled = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); - enabled = Objects.equals(packageName, service.getServicePackageName()); + + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + enabled = Objects.equals(packageName, service.getServicePackageName()); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override @@ -1891,8 +2023,9 @@ public final class AutofillManagerService || operation == AutofillManager.PENDING_UI_OPERATION_RESTORE, "invalid operation: %d", operation); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked( - UserHandle.getCallingUserId()); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); if (service != null) { service.onPendingSaveUi(operation, token); } @@ -1907,7 +2040,7 @@ public final class AutofillManagerService boolean uiOnly = false; if (args != null) { for (String arg : args) { - switch(arg) { + switch (arg) { case "--no-history": showHistory = false; break; @@ -1934,27 +2067,38 @@ public final class AutofillManagerService try { sDebug = sVerbose = true; synchronized (mLock) { - pw.print("sDebug: "); pw.print(realDebug); - pw.print(" sVerbose: "); pw.println(realVerbose); + pw.print("sDebug: "); + pw.print(realDebug); + pw.print(" sVerbose: "); + pw.println(realVerbose); pw.print("Flags: "); synchronized (mFlagLock) { - pw.print("mPccClassificationEnabled="); pw.print(mPccClassificationEnabled); + pw.print("mPccClassificationEnabled="); + pw.print(mPccClassificationEnabled); pw.print(";"); - pw.print("mPccPreferProviderOverPcc="); pw.print(mPccPreferProviderOverPcc); + pw.print("mPccPreferProviderOverPcc="); + pw.print(mPccPreferProviderOverPcc); pw.print(";"); - pw.print("mPccUseFallbackDetection="); pw.print(mPccUseFallbackDetection); + pw.print("mPccUseFallbackDetection="); + pw.print(mPccUseFallbackDetection); pw.print(";"); - pw.print("mPccProviderHints="); pw.println(mPccProviderHints); + pw.print("mPccProviderHints="); + pw.println(mPccProviderHints); } // Dump per-user services dumpLocked("", pw); - mAugmentedAutofillResolver.dumpShort(pw); pw.println(); - pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount); - pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount); + mAugmentedAutofillResolver.dumpShort(pw); + pw.println(); + pw.print("Max partitions per session: "); + pw.println(sPartitionMaxCount); + pw.print("Max visible datasets: "); + pw.println(sVisibleDatasetsMaxCount); if (sFullScreenMode != null) { - pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode); + pw.print("Overridden full-screen mode: "); + pw.println(sFullScreenMode); } - pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw); + pw.println("User data constraints: "); + UserData.dumpConstraints(prefix, pw); mUi.dump(pw); pw.print("Autofill Compat State: "); mAutofillCompatState.dump(prefix, pw); @@ -1969,11 +2113,17 @@ public final class AutofillManagerService pw.print("Augmented Service Request Timeout: "); pw.println(mAugmentedServiceRequestTimeoutMs); if (showHistory) { - pw.println(); pw.println("Requests history:"); pw.println(); + pw.println(); + pw.println("Requests history:"); + pw.println(); mRequestsHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("UI latency history:"); pw.println(); + pw.println(); + pw.println("UI latency history:"); + pw.println(); mUiLatencyHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("WTF history:"); pw.println(); + pw.println(); + pw.println("WTF history:"); + pw.println(); mWtfHistory.reverseDump(fd, pw, args); } pw.println("Augmented Autofill State: "); diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 44e198b53761..8c31209aeeb4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -1678,9 +1678,9 @@ final class ActivityManagerConstants extends ContentObserver { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME, DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS); - if (mKillBgRestrictedAndCachedIdleSettleTimeMs != currentSettleTime) { - mService.mHandler.removeMessages( - ActivityManagerService.IDLE_UIDS_MSG); + if (mKillBgRestrictedAndCachedIdleSettleTimeMs < currentSettleTime) { + // Don't remove existing messages in case other IDLE_UIDS_MSG initiators use lower + // delays, but send a new message if the settle time has decreased. mService.mHandler.sendEmptyMessageDelayed( ActivityManagerService.IDLE_UIDS_MSG, mKillBgRestrictedAndCachedIdleSettleTimeMs); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 12eefbaa8444..b99e619e19fa 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1526,6 +1526,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ int mBootPhase; + volatile boolean mDeterministicUidIdle = false; + @VisibleForTesting public WindowManagerService mWindowManager; WindowManagerInternal mWmInternal; @@ -16501,6 +16503,11 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void setDeterministicUidIdle(boolean deterministic) { + mDeterministicUidIdle = deterministic; + } + /** Make the currently active UIDs idle after a certain grace period. */ final void idleUids() { synchronized (this) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index add22bd6009e..fd980727e12b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -290,6 +290,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runKillAll(pw); case "make-uid-idle": return runMakeIdle(pw); + case "set-deterministic-uid-idle": + return runSetDeterministicUidIdle(pw); case "monitor": return runMonitor(pw); case "watch-uids": @@ -1520,6 +1522,23 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runSetDeterministicUidIdle(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + boolean deterministic = Boolean.parseBoolean(getNextArgRequired()); + mInterface.setDeterministicUidIdle(deterministic); + return 0; + } + static final class MyActivityController extends IActivityController.Stub { final IActivityManager mInterface; final PrintWriter mPw; @@ -4271,6 +4290,11 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>"); pw.println(" If the given application's uid is in the background and waiting to"); pw.println(" become idle (not allowing background services), do that now."); + pw.println( + " set-deterministic-uid-idle [--user <USER_ID> | all | current] <true|false>"); + pw.println(" If true, sets the timing of making UIDs idle consistent and"); + pw.println(" deterministic. If false, the timing will be variable depending on"); + pw.println(" other activity on the device. The default is false."); pw.println(" monitor [--gdb <port>] [-p <TARGET>] [-s] [-c] [-k]"); pw.println(" Start monitoring for crashes or ANRs."); pw.println(" --gdb: start gdbserv on the given port at crash/ANR"); diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index 397eef4216a1..76af50de0bd7 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList; +import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.getReceiverClassName; import static com.android.server.am.BroadcastRecord.getReceiverPackageName; @@ -68,6 +69,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.MathUtils; @@ -213,6 +215,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { new AtomicReference<>(); /** + * Container for holding the set of broadcast records that satisfied a certain criteria. + */ + @GuardedBy("mService") + private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mRecordsLookupCache = + new AtomicReference<>(); + + /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. * <p> @@ -742,13 +751,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { broadcastConsumer = mBroadcastConsumerSkipAndCanceled; break; case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED: + // TODO: Allow applying MERGED policy for broadcasts with more than one receiver. + if (r.receivers.size() > 1) { + return; + } final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger(); if (extrasMerger == null) { // Extras merger is required to be able to merge the extras. So, if it's not // supplied, then ignore the delivery group policy. return; } - // TODO: Don't merge with the same BroadcastRecord more than once. broadcastConsumer = (record, recordIndex) -> { r.intent.mergeExtras(record.intent, extrasMerger); mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex); @@ -758,6 +770,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { logw("Unknown delivery group policy: " + policy); return; } + final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache(); forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { // If the receiver is already in a terminal state, then ignore it. if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) { @@ -769,22 +782,44 @@ class BroadcastQueueModernImpl extends BroadcastQueue { || !r.matchesDeliveryGroup(testRecord)) { return false; } - // TODO: If a process is in a deferred state, we can always apply the policy as long - // as it is one of the receivers for the new broadcast. // For ordered broadcast, check if the receivers for the new broadcast is a superset // of those for the previous one as skipping and removing only one of them could result // in an inconsistent state. - if (testRecord.ordered || testRecord.resultTo != null) { - // TODO: Cache this result in some way so that we don't have to perform the - // same check for all the broadcast receivers. - return r.containsAllReceivers(testRecord.receivers); - } else if (testRecord.prioritized) { - return r.containsAllReceivers(testRecord.receivers); + if (testRecord.ordered || testRecord.prioritized) { + return containsAllReceivers(r, testRecord, recordsLookupCache); + } else if (testRecord.resultTo != null) { + return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED + ? r.containsReceiver(testRecord.receivers.get(testIndex)) + : containsAllReceivers(r, testRecord, recordsLookupCache); } else { return r.containsReceiver(testRecord.receivers.get(testIndex)); } }, broadcastConsumer, true); + recordsLookupCache.clear(); + mRecordsLookupCache.compareAndSet(null, recordsLookupCache); + } + + @NonNull + private ArrayMap<BroadcastRecord, Boolean> getRecordsLookupCache() { + ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = + mRecordsLookupCache.getAndSet(null); + if (recordsLookupCache == null) { + recordsLookupCache = new ArrayMap<>(); + } + return recordsLookupCache; + } + + private boolean containsAllReceivers(@NonNull BroadcastRecord record, + @NonNull BroadcastRecord testRecord, + @NonNull ArrayMap<BroadcastRecord, Boolean> recordsLookupCache) { + final int idx = recordsLookupCache.indexOfKey(testRecord); + if (idx > 0) { + return recordsLookupCache.valueAt(idx); + } + final boolean containsAll = record.containsAllReceivers(testRecord.receivers); + recordsLookupCache.put(testRecord, containsAll); + return containsAll; } /** diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a86c2e362c54..764bbe8bd191 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1471,7 +1471,8 @@ public class OomAdjuster { if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState()) || uidRec.isSetAllowListed()) { uidRec.setLastBackgroundTime(nowElapsed); - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { // Note: the background settle time is in elapsed realtime, while // the handler time base is uptime. All this means is that we may // stop background uids later than we had intended, but that only @@ -3227,7 +3228,8 @@ public class OomAdjuster { // (for states debouncing to avoid from thrashing). state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed); // Kick off the delayed checkup message if needed. - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs); } @@ -3346,6 +3348,7 @@ public class OomAdjuster { @GuardedBy("mService") void idleUidsLocked() { final int N = mActiveUids.size(); + mService.mHandler.removeMessages(IDLE_UIDS_MSG); if (N <= 0) { return; } @@ -3391,7 +3394,6 @@ public class OomAdjuster { } } if (nextTime > 0) { - mService.mHandler.removeMessages(IDLE_UIDS_MSG); mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4342cb994754..c5776d822c8f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2572,9 +2572,14 @@ public final class ProcessList { // and did the cleanup before the actual death notification. Check the dying processes. predecessor = mDyingProcesses.get(processName, info.uid); if (predecessor != null) { - if (app != null) { + // The process record could have existed but its pid is set to 0. In this case, + // the 'app' and 'predecessor' could end up pointing to the same instance; + // so make sure we check this case here. + if (app != null && app != predecessor) { app.mPredecessor = predecessor; predecessor.mSuccessor = app; + } else { + app = null; } Slog.w(TAG_PROCESSES, predecessor.toString() + " is attached to a previous process " + predecessor.getDyingPid()); @@ -5195,6 +5200,8 @@ public final class ProcessList { mDyingProcesses.remove(app.processName, app.uid); app.setDyingPid(0); handlePrecedingAppDiedLocked(app); + // Remove from the LRU list if it's still there. + removeLruProcessLocked(app); return true; } return false; @@ -5243,7 +5250,9 @@ public final class ProcessList { mAppsInBackgroundRestricted.add(app); final long future = killAppIfBgRestrictedAndCachedIdleLocked( app, nowElapsed); - if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (future > 0 + && (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG))) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, future - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 0f5defbef919..50fe6d71d26e 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -1076,12 +1076,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (app == null) { return; } - if (mBackgroundStartPrivilegesByStartMerged.allowsAny() - || mIsAllowedBgActivityStartsByBinding) { + BackgroundStartPrivileges backgroundStartPrivileges = + getBackgroundStartPrivilegesWithExclusiveToken(); + if (backgroundStartPrivileges.allowsAny()) { // if the token is already there it's safe to "re-add it" - we're dealing with // a set of Binder objects app.addOrUpdateBackgroundStartPrivileges(this, - getBackgroundStartPrivilegesWithExclusiveToken()); + backgroundStartPrivileges); } else { app.removeBackgroundStartPrivileges(this); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b39e8606e189..d29d9c84501d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6597,7 +6597,10 @@ public class NotificationManagerService extends SystemService { } private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) { - if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION)) { + if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION) + && Binder.withCleanCallingIdentity( + () -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, false))) { // The package probably doesn't have WAKE_LOCK permission and should not require it. return Binder.withCleanCallingIdentity(() -> { WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 43f96e7dcead..99064bc1884d 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -589,6 +589,8 @@ public class ThermalManagerService extends SystemService { @Override public int onCommand(String cmd) { switch(cmd != null ? cmd : "") { + case "inject-temperature": + return runInjectTemperature(); case "override-status": return runOverrideStatus(); case "reset": @@ -611,6 +613,95 @@ public class ThermalManagerService extends SystemService { } } + + private int runInjectTemperature() { + final long token = Binder.clearCallingIdentity(); + try { + final PrintWriter pw = getOutPrintWriter(); + int type; + String typeName = getNextArgRequired(); + switch (typeName.toUpperCase()) { + case "UNKNOWN": + type = Temperature.TYPE_UNKNOWN; + break; + case "CPU": + type = Temperature.TYPE_CPU; + break; + case "GPU": + type = Temperature.TYPE_GPU; + break; + case "BATTERY": + type = Temperature.TYPE_BATTERY; + break; + case "SKIN": + type = Temperature.TYPE_SKIN; + break; + case "USB_PORT": + type = Temperature.TYPE_USB_PORT; + break; + case "POWER_AMPLIFIER": + type = Temperature.TYPE_POWER_AMPLIFIER; + break; + case "BCL_VOLTAGE": + type = Temperature.TYPE_BCL_VOLTAGE; + break; + case "BCL_CURRENT": + type = Temperature.TYPE_BCL_CURRENT; + break; + case "BCL_PERCENTAGE": + type = Temperature.TYPE_BCL_PERCENTAGE; + break; + case "NPU": + type = Temperature.TYPE_NPU; + break; + default: + pw.println("Invalid temperature type: " + typeName); + return -1; + } + int throttle; + String throttleName = getNextArgRequired(); + switch (throttleName.toUpperCase()) { + case "NONE": + throttle = Temperature.THROTTLING_NONE; + break; + case "LIGHT": + throttle = Temperature.THROTTLING_LIGHT; + break; + case "MODERATE": + throttle = Temperature.THROTTLING_MODERATE; + break; + case "SEVERE": + throttle = Temperature.THROTTLING_SEVERE; + break; + case "CRITICAL": + throttle = Temperature.THROTTLING_CRITICAL; + break; + case "EMERGENCY": + throttle = Temperature.THROTTLING_EMERGENCY; + break; + case "SHUTDOWN": + throttle = Temperature.THROTTLING_SHUTDOWN; + break; + default: + pw.println("Invalid throttle status: " + throttleName); + return -1; + } + String name = getNextArgRequired(); + float value = 28.0f; + try { + String valueStr = getNextArg(); + if (valueStr != null) value = Float.parseFloat(valueStr); + } catch (RuntimeException ex) { + pw.println("Error: " + ex.toString()); + return -1; + } + onTemperatureChanged(new Temperature(value, type, name, throttle), true); + return 0; + } finally { + Binder.restoreCallingIdentity(token); + } + } + private int runOverrideStatus() { final long token = Binder.clearCallingIdentity(); try { @@ -643,6 +734,9 @@ public class ThermalManagerService extends SystemService { pw.println(" help"); pw.println(" Print this help text."); pw.println(""); + pw.println(" inject-temperature TYPE STATUS NAME [VALUE]"); + pw.println(" injects a new temperature sample for the specified device."); + pw.println(" type and status strings follow the names in android.os.Temperature."); pw.println(" override-status STATUS"); pw.println(" sets and locks the thermal status of the device to STATUS."); pw.println(" status code is defined in android.os.Temperature."); diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 7e783938d30a..01158779c24f 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -185,6 +185,8 @@ class AsyncRotationController extends FadeAnimationController implements Consume } } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS) { action = Operation.ACTION_SEAMLESS; + } else if (mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) { + return; } mTargetWindowTokens.put(w.mToken, new Operation(action)); return; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 795d022512a3..aad12251502d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -234,9 +234,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { private @TransitionState int mState = STATE_PENDING; private final ReadyTracker mReadyTracker = new ReadyTracker(); - // TODO(b/188595497): remove when not needed. - /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ - private boolean mNavBarAttachedToApp = false; private int mRecentsDisplayId = INVALID_DISPLAY; /** The delay for light bar appearance animation. */ @@ -1781,7 +1778,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (navWindow == null || navWindow.mToken == null) { return; } - mNavBarAttachedToApp = true; + mController.mNavigationBarAttachedToApp = true; navWindow.mToken.cancelAnimation(); final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); @@ -1803,8 +1800,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** @see RecentsAnimationController#restoreNavigationBarFromApp */ void legacyRestoreNavigationBarFromApp() { - if (!mNavBarAttachedToApp) return; - mNavBarAttachedToApp = false; + if (!mController.mNavigationBarAttachedToApp) { + return; + } + mController.mNavigationBarAttachedToApp = false; if (mRecentsDisplayId == INVALID_DISPLAY) { Slog.e(TAG, "Reparented navigation bar without a valid display"); @@ -1837,6 +1836,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { break; } + final AsyncRotationController asyncRotationController = dc.getAsyncRotationController(); + if (asyncRotationController != null) { + asyncRotationController.accept(navWindow); + } + if (animate) { final NavBarFadeAnimationController controller = new NavBarFadeAnimationController(dc); @@ -1845,6 +1849,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Reparent the SurfaceControl of nav bar token back. t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); } + + // To apply transactions. + dc.mWmService.scheduleAnimationLocked(); } private void reportStartReasonsToLogger() { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 0cb6f14b38f2..359b353ba336 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -206,6 +206,11 @@ class TransitionController { */ boolean mBuildingFinishLayers = false; + /** + * Whether the surface of navigation bar token is reparented to an app. + */ + boolean mNavigationBarAttachedToApp = false; + private boolean mAnimatingState = false; final Handler mLoggerHandler = FgThread.getHandler(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 3672820c13ad..d7d2b4e9dde2 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -563,7 +563,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } - final long diff = lastLaunchTime - launchTime; + final long diff = launchTime - lastLaunchTime; if (diff < RAPID_ACTIVITY_LAUNCH_MS) { mRapidActivityLaunchCount++; } else if (diff >= RESET_RAPID_ACTIVITY_LAUNCH_MS) { diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 Binary files differindex 138b6113ea6b..3683dca64449 100644 --- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 +++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java index ca4a4048cc00..dc92376263a6 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java @@ -66,7 +66,6 @@ import android.system.StructStat; import android.test.AndroidTestCase; import android.util.Log; -import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; import androidx.test.filters.SmallTest; import androidx.test.filters.Suppress; @@ -2507,7 +2506,6 @@ public class PackageManagerTests extends AndroidTestCase { } @LargeTest - @FlakyTest(bugId = 283797480) public void testCheckSignaturesRotatedAgainstRotated() throws Exception { // checkSignatures should be successful when both apps have been signed with the same // rotated key since the initial signature comparison between the two apps should diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 202e7e7d2699..1542112f7c99 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -1094,6 +1094,17 @@ public final class BroadcastQueueModernImplTest { verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), resultTo, false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_resultTo_diffReceivers"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), resultTo, false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); } @Test @@ -1279,6 +1290,36 @@ public final class BroadcastQueueModernImplTest { } @Test + public void testDeliveryGroupPolicy_merged_multipleReceivers() { + final long now = SystemClock.elapsedRealtime(); + final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast1 = createDropboxBroadcast( + "TAG_A", now, 2); + final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast2 = createDropboxBroadcast( + "TAG_A", now + 1000, 4); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first, + dropboxEntryBroadcast1.second, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_RED, CLASS_RED)), + false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first, + dropboxEntryBroadcast2.second, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_RED, CLASS_RED)), + false)); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + + verifyPendingRecords(greenQueue, + List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); + verifyPendingRecords(redQueue, + List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); + } + + @Test public void testDeliveryGroupPolicy_sameAction_differentMatchingCriteria() { final Intent closeSystemDialogs1 = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); final BroadcastOptions optionsCloseSystemDialog1 = BroadcastOptions.makeBasic() diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 2a0c745a0ffc..cebc5409c795 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -587,6 +587,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { return wl; }); mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); // apps allowed as convos mService.setStringArrayResourceValue(PKG_O); @@ -1929,8 +1931,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void enqueueNotification_wakeLockFlagOff_noWakeLock() throws Exception { + public void enqueueNotification_wakeLockSystemPropertyOff_noWakeLock() throws Exception { mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + verifyZeroInteractions(mPowerManager); + } + + @Test + public void enqueueNotification_wakeLockDeviceConfigOff_noWakeLock() throws Exception { + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "false", false); mBinderService.enqueueNotificationWithTag(PKG, PKG, "enqueueNotification_setsWakeLockWorkSource", 0, |