diff options
66 files changed, 1678 insertions, 747 deletions
diff --git a/api/current.txt b/api/current.txt index 17fbb55d2f91..69700f3b0a60 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5596,6 +5596,7 @@ package android.app { method public android.graphics.drawable.Icon getIcon(); method public android.app.RemoteInput[] getRemoteInputs(); method public int getSemanticAction(); + method public boolean isAuthenticationRequired(); method public boolean isContextual(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR; @@ -5625,6 +5626,7 @@ package android.app { method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender); method @NonNull public android.os.Bundle getExtras(); method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean); + method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean); method @NonNull public android.app.Notification.Action.Builder setContextual(boolean); method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int); } diff --git a/api/system-current.txt b/api/system-current.txt index b011e9af40a0..721523c95ecb 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -301,6 +301,7 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } diff --git a/api/test-current.txt b/api/test-current.txt index 963ef7c540c7..3332525a6647 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42,6 +42,7 @@ package android { public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 7cec717f96e0..d67b98620f37 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -424,9 +424,14 @@ public final class ActivityThread extends ClientTransactionHandler { final String authority; final int userId; + @GuardedBy("mLock") + ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter + Object mLock; // The lock to be used to get notified when the provider is ready + public ProviderKey(String authority, int userId) { this.authority = authority; this.userId = userId; + this.mLock = new Object(); } @Override @@ -440,7 +445,11 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public int hashCode() { - return ((authority != null) ? authority.hashCode() : 0) ^ userId; + return hashCode(authority, userId); + } + + public static int hashCode(final String auth, final int userIdent) { + return ((auth != null) ? auth.hashCode() : 0) ^ userIdent; } } @@ -461,9 +470,8 @@ public final class ActivityThread extends ClientTransactionHandler { // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider(). // Note we never removes items from this map but that's okay because there are only so many // users and so many authorities. - // TODO Remove it once we move CPR.wait() from AMS to the client side. - @GuardedBy("mGetProviderLocks") - final ArrayMap<ProviderKey, Object> mGetProviderLocks = new ArrayMap<>(); + @GuardedBy("mGetProviderKeys") + final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>(); final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); @@ -1756,6 +1764,16 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityThread.this, activityToken, actionId, arguments, cancellationSignal, resultCallback)); } + + @Override + public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, + @NonNull String auth, int userId, boolean published) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key.mLock) { + key.mHolder = holder; + key.mLock.notifyAll(); + } + } } private @NonNull SafeCancellationTransport createSafeCancellationTransport( @@ -6807,13 +6825,33 @@ public final class ActivityThread extends ClientTransactionHandler { // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. ContentProviderHolder holder = null; + final ProviderKey key = getGetProviderKey(auth, userId); try { - synchronized (getGetProviderLock(auth, userId)) { + synchronized (key) { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), c.getOpPackageName(), auth, userId, stable); + // If the returned holder is non-null but its provider is null and it's not + // local, we'll need to wait for the publishing of the provider. + if (holder != null && holder.provider == null && !holder.mLocal) { + synchronized (key.mLock) { + key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + holder = key.mHolder; + } + if (holder != null && holder.provider == null) { + // probably timed out + holder = null; + } + } } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); + } catch (InterruptedException e) { + holder = null; + } finally { + // Clear the holder from the key since the key itself is never cleared. + synchronized (key.mLock) { + key.mHolder = null; + } } if (holder == null) { if (UserManager.get(c).isUserUnlocked(userId)) { @@ -6831,13 +6869,13 @@ public final class ActivityThread extends ClientTransactionHandler { return holder.provider; } - private Object getGetProviderLock(String auth, int userId) { - final ProviderKey key = new ProviderKey(auth, userId); - synchronized (mGetProviderLocks) { - Object lock = mGetProviderLocks.get(key); + private ProviderKey getGetProviderKey(String auth, int userId) { + final int key = ProviderKey.hashCode(auth, userId); + synchronized (mGetProviderKeys) { + ProviderKey lock = mGetProviderKeys.get(key); if (lock == null) { - lock = key; - mGetProviderLocks.put(key, lock); + lock = new ProviderKey(auth, userId); + mGetProviderKeys.put(key, lock); } return lock; } diff --git a/core/java/android/app/ContentProviderHolder.java b/core/java/android/app/ContentProviderHolder.java index 3d745831ce1c..e330a30de7b0 100644 --- a/core/java/android/app/ContentProviderHolder.java +++ b/core/java/android/app/ContentProviderHolder.java @@ -39,6 +39,11 @@ public class ContentProviderHolder implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public boolean noReleaseNeeded; + /** + * Whether the provider here is a local provider or not. + */ + public boolean mLocal; + @UnsupportedAppUsage public ContentProviderHolder(ProviderInfo _info) { info = _info; @@ -59,6 +64,7 @@ public class ContentProviderHolder implements Parcelable { } dest.writeStrongBinder(connection); dest.writeInt(noReleaseNeeded ? 1 : 0); + dest.writeInt(mLocal ? 1 : 0); } public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR @@ -81,5 +87,6 @@ public class ContentProviderHolder implements Parcelable { source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; + mLocal = source.readInt() != 0; } -}
\ No newline at end of file +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 93dfc79109a6..f37ca61ba69f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -461,7 +461,7 @@ interface IActivityManager { @UnsupportedAppUsage Rect getTaskBounds(int taskId); @UnsupportedAppUsage - boolean setProcessMemoryTrimLevel(in String process, int uid, int level); + boolean setProcessMemoryTrimLevel(in String process, int userId, int level); // Start of L transactions diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 24da50481df3..dc9918ade9c2 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -16,6 +16,7 @@ package android.app; +import android.app.ContentProviderHolder; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; @@ -147,4 +148,6 @@ oneway interface IApplicationThread { void performDirectAction(IBinder activityToken, String actionId, in Bundle arguments, in RemoteCallback cancellationCallback, in RemoteCallback resultCallback); + void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth, + int userId, boolean published); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 68e65612971c..6f3e89229e4c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1492,6 +1492,7 @@ public class Notification implements Parcelable private boolean mAllowGeneratedReplies = true; private final @SemanticAction int mSemanticAction; private final boolean mIsContextual; + private boolean mAuthenticationRequired; /** * Small icon representing the action. @@ -1528,6 +1529,7 @@ public class Notification implements Parcelable mAllowGeneratedReplies = in.readInt() == 1; mSemanticAction = in.readInt(); mIsContextual = in.readInt() == 1; + mAuthenticationRequired = in.readInt() == 1; } /** @@ -1536,13 +1538,14 @@ public class Notification implements Parcelable @Deprecated public Action(int icon, CharSequence title, PendingIntent intent) { this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true, - SEMANTIC_ACTION_NONE, false /* isContextual */); + SEMANTIC_ACTION_NONE, false /* isContextual */, false /* requireAuth */); } /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */ private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs, boolean allowGeneratedReplies, - @SemanticAction int semanticAction, boolean isContextual) { + @SemanticAction int semanticAction, boolean isContextual, + boolean requireAuth) { this.mIcon = icon; if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { this.icon = icon.getResId(); @@ -1554,6 +1557,7 @@ public class Notification implements Parcelable this.mAllowGeneratedReplies = allowGeneratedReplies; this.mSemanticAction = semanticAction; this.mIsContextual = isContextual; + this.mAuthenticationRequired = requireAuth; } /** @@ -1624,6 +1628,17 @@ public class Notification implements Parcelable } /** + * Returns whether the OS should only send this action's {@link PendingIntent} on an + * unlocked device. + * + * If the device is locked when the action is invoked, the OS should show the keyguard and + * require successful authentication before invoking the intent. + */ + public boolean isAuthenticationRequired() { + return mAuthenticationRequired; + } + + /** * Builder class for {@link Action} objects. */ public static final class Builder { @@ -1635,6 +1650,7 @@ public class Notification implements Parcelable @Nullable private ArrayList<RemoteInput> mRemoteInputs; private @SemanticAction int mSemanticAction; private boolean mIsContextual; + private boolean mAuthenticationRequired; /** * Construct a new builder for {@link Action} object. @@ -1654,7 +1670,7 @@ public class Notification implements Parcelable * @param intent the {@link PendingIntent} to fire when users trigger this action */ public Builder(Icon icon, CharSequence title, PendingIntent intent) { - this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE); + this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, false); } /** @@ -1665,23 +1681,25 @@ public class Notification implements Parcelable public Builder(Action action) { this(action.getIcon(), action.title, action.actionIntent, new Bundle(action.mExtras), action.getRemoteInputs(), - action.getAllowGeneratedReplies(), action.getSemanticAction()); + action.getAllowGeneratedReplies(), action.getSemanticAction(), + action.isAuthenticationRequired()); } private Builder(@Nullable Icon icon, @Nullable CharSequence title, @Nullable PendingIntent intent, @NonNull Bundle extras, @Nullable RemoteInput[] remoteInputs, boolean allowGeneratedReplies, - @SemanticAction int semanticAction) { + @SemanticAction int semanticAction, boolean authRequired) { mIcon = icon; mTitle = title; mIntent = intent; mExtras = extras; if (remoteInputs != null) { - mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length); + mRemoteInputs = new ArrayList<>(remoteInputs.length); Collections.addAll(mRemoteInputs, remoteInputs); } mAllowGeneratedReplies = allowGeneratedReplies; mSemanticAction = semanticAction; + mAuthenticationRequired = authRequired; } /** @@ -1776,6 +1794,21 @@ public class Notification implements Parcelable } /** + * Sets whether the OS should only send this action's {@link PendingIntent} on an + * unlocked device. + * + * If this is true and the device is locked when the action is invoked, the OS will + * show the keyguard and require successful authentication before invoking the intent. + * If this is false and the device is locked, the OS will decide whether authentication + * should be required. + */ + @NonNull + public Builder setAuthenticationRequired(boolean authenticationRequired) { + mAuthenticationRequired = authenticationRequired; + return this; + } + + /** * Throws an NPE if we are building a contextual action missing one of the fields * necessary to display the action. */ @@ -1827,7 +1860,8 @@ public class Notification implements Parcelable RemoteInput[] textInputsArr = textInputs.isEmpty() ? null : textInputs.toArray(new RemoteInput[textInputs.size()]); return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr, - mAllowGeneratedReplies, mSemanticAction, mIsContextual); + mAllowGeneratedReplies, mSemanticAction, mIsContextual, + mAuthenticationRequired); } } @@ -1841,7 +1875,8 @@ public class Notification implements Parcelable getRemoteInputs(), getAllowGeneratedReplies(), getSemanticAction(), - isContextual()); + isContextual(), + isAuthenticationRequired()); } @Override @@ -1870,6 +1905,7 @@ public class Notification implements Parcelable out.writeInt(mAllowGeneratedReplies ? 1 : 0); out.writeInt(mSemanticAction); out.writeInt(mIsContextual ? 1 : 0); + out.writeInt(mAuthenticationRequired ? 1 : 0); } public static final @android.annotation.NonNull Parcelable.Creator<Action> CREATOR = diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 3522b1b8aff5..0e6a0637d801 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -199,7 +199,7 @@ public final class UsageEvents implements Parcelable { public static final int NOTIFICATION_INTERRUPTION = 12; /** - * A Slice was pinned by the default launcher or the default assistant. + * A Slice was pinned by the default assistant. * @hide */ @SystemApi diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 9bf05135c4c5..a23fc4b57b45 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -291,7 +291,7 @@ public class ScreenshotHelper { }; Message msg = Message.obtain(null, screenshotType, screenshotRequest); - final ServiceConnection myConn = mScreenshotConnection; + Handler h = new Handler(handler.getLooper()) { @Override public void handleMessage(Message msg) { @@ -304,8 +304,8 @@ public class ScreenshotHelper { break; case SCREENSHOT_MSG_PROCESS_COMPLETE: synchronized (mScreenshotLock) { - if (myConn != null && mScreenshotConnection == myConn) { - mContext.unbindService(myConn); + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); mScreenshotConnection = null; mScreenshotService = null; } @@ -368,6 +368,7 @@ public class ScreenshotHelper { } } else { Messenger messenger = new Messenger(mScreenshotService); + try { messenger.send(msg); } catch (RemoteException e) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index fe290f3e97e8..edb972730f69 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1342,7 +1342,7 @@ android:priority="800" /> <!-- Allows an application to access data from sensors that the user uses to - measure what is happening inside his/her body, such as heart rate. + measure what is happening inside their body, such as heart rate. <p>Protection level: dangerous --> <permission android:name="android.permission.BODY_SENSORS" android:permissionGroup="android.permission-group.UNDEFINED" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8913e8b18750..f99be880cbe3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1823,6 +1823,8 @@ <string name="config_defaultCallScreening" translatable="false"></string> <!-- The name of the package that will hold the system gallery role. --> <string name="config_systemGallery" translatable="false">com.android.gallery3d</string> + <!-- The name of the package that will hold the system cluster service role. --> + <string name="config_systemAutomotiveCluster" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 303fde6705c2..1e2d554a088b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3069,7 +3069,8 @@ </public-group> <public-group type="string" first-id="0x01040028"> - <!-- string definitions go here --> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemAutomotiveCluster" /> </public-group> <public-group type="id" first-id="0x01020055"> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index e0d702e98595..2bf9848304ad 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -24,6 +24,7 @@ import static android.app.servertransaction.TestUtils.resultInfoList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.app.ContentProviderHolder; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; @@ -665,5 +666,10 @@ public class TransactionParcelTests { public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments, RemoteCallback cancellationCallback, RemoteCallback resultCallback) { } + + @Override + public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth, + int userId, boolean published) { + } } } diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index 5d4404036102..e1bff03f6833 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -244,7 +244,7 @@ public final class MediaTranscodeManager { } // Updates the job progress. - job.setJobProgress(newProgress); + job.updateProgress(newProgress); // Notifies client the progress update. if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) { @@ -254,6 +254,21 @@ public final class MediaTranscodeManager { } } + private void updateStatus(int jobId, int status) { + synchronized (mPendingTranscodingJobs) { + final TranscodingJob job = mPendingTranscodingJobs.get(jobId); + + if (job == null) { + // This should not happen in reality. + Log.e(TAG, "Job " + jobId + " is not in PendingJobs"); + return; + } + + // Updates the job status. + job.updateStatus(status); + } + } + // Just forwards all the events to the event handler. private ITranscodingClientCallback mTranscodingClientCallback = new ITranscodingClientCallback.Stub() { @@ -285,17 +300,17 @@ public final class MediaTranscodeManager { @Override public void onTranscodingStarted(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_RUNNING); } @Override public void onTranscodingPaused(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_PAUSED); } @Override public void onTranscodingResumed(int jobId) throws RemoteException { - + updateStatus(jobId, TranscodingJob.STATUS_RUNNING); } @Override @@ -683,11 +698,14 @@ public final class MediaTranscodeManager { public static final int STATUS_RUNNING = 2; /** The job is finished. */ public static final int STATUS_FINISHED = 3; + /** The job is paused. */ + public static final int STATUS_PAUSED = 4; @IntDef(prefix = { "STATUS_" }, value = { STATUS_PENDING, STATUS_RUNNING, STATUS_FINISHED, + STATUS_PAUSED, }) @Retention(RetentionPolicy.SOURCE) public @interface Status {} @@ -843,9 +861,13 @@ public final class MediaTranscodeManager { return mResult; } - private synchronized void setJobProgress(int newProgress) { + private synchronized void updateProgress(int newProgress) { mProgress = newProgress; } + + private synchronized void updateStatus(int newStatus) { + mStatus = newStatus; + } } /** diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java index 8e4153078174..8fe10888cab6 100644 --- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java +++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java @@ -33,6 +33,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @@ -238,6 +239,7 @@ public class MediaTranscodeManagerTest Log.d(TAG, "Starting: testMediaTranscodeManager"); Semaphore transcodeCompleteSemaphore = new Semaphore(0); + final CountDownLatch statusLatch = new CountDownLatch(1); // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4 // The full path of this file is: @@ -276,12 +278,21 @@ public class MediaTranscodeManagerTest public void onProgressUpdate(int newProgress) { assertTrue("Invalid proress update", newProgress > mPreviousProgress); assertTrue("Invalid proress update", newProgress <= 100); + if (newProgress > 0) { + statusLatch.countDown(); + } mPreviousProgress = newProgress; progressUpdateCount.getAndIncrement(); Log.i(TAG, "Get progress update " + newProgress); } }); + try { + statusLatch.await(); + // The transcoding should not be finished yet as the clip is long. + assertTrue("Invalid status", job.getStatus() == TranscodingJob.STATUS_RUNNING); + } catch (InterruptedException e) { } + Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel."); boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire( TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS); diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index e06cf945ddaa..51e7287bfd72 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -5596,6 +5596,7 @@ package android.app { method public android.graphics.drawable.Icon getIcon(); method public android.app.RemoteInput[] getRemoteInputs(); method public int getSemanticAction(); + method public boolean isAuthenticationRequired(); method public boolean isContextual(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR; @@ -5625,6 +5626,7 @@ package android.app { method @NonNull public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender); method @NonNull public android.os.Bundle getExtras(); method @NonNull public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean); + method @NonNull public android.app.Notification.Action.Builder setAuthenticationRequired(boolean); method @NonNull public android.app.Notification.Action.Builder setContextual(boolean); method @NonNull public android.app.Notification.Action.Builder setSemanticAction(int); } diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 0906a060c40f..cf792b6abbd2 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -301,6 +301,7 @@ package android { field public static final int config_helpIntentNameKey = 17039390; // 0x104001e field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c + field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java new file mode 100644 index 000000000000..1de740a083c2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextUtils.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.annotation.UserIdInt; +import android.content.Context; + +public class ContextUtils { + + /** Get the user associated with this context */ + public static @UserIdInt int getUserId(Context context) { + return context.getUserId(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java index 7e5b42653210..93a8df41c673 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java @@ -25,7 +25,9 @@ public class AppOpItem { private int mUid; private String mPackageName; private long mTimeStarted; - private String mState; + private StringBuilder mState; + // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO + private boolean mSilenced; public AppOpItem(int code, int uid, String packageName, long timeStarted) { this.mCode = code; @@ -36,9 +38,8 @@ public class AppOpItem { .append("AppOpItem(") .append("Op code=").append(code).append(", ") .append("UID=").append(uid).append(", ") - .append("Package name=").append(packageName) - .append(")") - .toString(); + .append("Package name=").append(packageName).append(", ") + .append("Paused="); } public int getCode() { @@ -57,8 +58,16 @@ public class AppOpItem { return mTimeStarted; } + public void setSilenced(boolean silenced) { + mSilenced = silenced; + } + + public boolean isSilenced() { + return mSilenced; + } + @Override public String toString() { - return mState; + return mState.append(mSilenced).append(")").toString(); } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 6512624f5064..8187a2235c0a 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -19,6 +19,8 @@ package com.android.systemui.appops; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.media.AudioRecordingConfiguration; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -63,6 +65,7 @@ public class AppOpsControllerImpl implements AppOpsController, private static final boolean DEBUG = false; private final AppOpsManager mAppOps; + private final AudioManager mAudioManager; private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>(); @@ -73,6 +76,9 @@ public class AppOpsControllerImpl implements AppOpsController, private final List<AppOpItem> mActiveItems = new ArrayList<>(); @GuardedBy("mNotedItems") private final List<AppOpItem> mNotedItems = new ArrayList<>(); + @GuardedBy("mActiveItems") + private final SparseArray<ArrayList<AudioRecordingConfiguration>> mRecordingsByUid = + new SparseArray<>(); protected static final int[] OPS = new int[] { AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, @@ -88,7 +94,8 @@ public class AppOpsControllerImpl implements AppOpsController, Context context, @Background Looper bgLooper, DumpManager dumpManager, - PermissionFlagsCache cache + PermissionFlagsCache cache, + AudioManager audioManager ) { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mFlagsCache = cache; @@ -97,6 +104,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numOps; i++) { mCallbacksByCode.put(OPS[i], new ArraySet<>()); } + mAudioManager = audioManager; dumpManager.registerDumpable(TAG, this); } @@ -111,12 +119,19 @@ public class AppOpsControllerImpl implements AppOpsController, if (listening) { mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); + mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); + mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( + mAudioManager.getActiveRecordingConfigurations())); + } else { mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); + mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); + mBGHandler.removeCallbacksAndMessages(null); // null removes all synchronized (mActiveItems) { mActiveItems.clear(); + mRecordingsByUid.clear(); } synchronized (mNotedItems) { mNotedItems.clear(); @@ -189,9 +204,12 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName); if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); + if (code == AppOpsManager.OP_RECORD_AUDIO) { + item.setSilenced(isAnyRecordingPausedLocked(uid)); + } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); - return true; + return !item.isSilenced(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); @@ -215,7 +233,7 @@ public class AppOpsControllerImpl implements AppOpsController, active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!active) { - notifySuscribers(code, uid, packageName, false); + notifySuscribersWorker(code, uid, packageName, false); } } @@ -324,7 +342,7 @@ public class AppOpsControllerImpl implements AppOpsController, AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item)) { + && isUserVisible(item) && !item.isSilenced()) { list.add(item); } } @@ -343,6 +361,10 @@ public class AppOpsControllerImpl implements AppOpsController, return list; } + private void notifySuscribers(int code, int uid, String packageName, boolean active) { + mBGHandler.post(() -> notifySuscribersWorker(code, uid, packageName, active)); + } + @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (DEBUG) { @@ -360,7 +382,7 @@ public class AppOpsControllerImpl implements AppOpsController, // If active is false, we only send the update if the op is not actively noted (prevent // early removal) if (!alsoNoted) { - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active)); + notifySuscribers(code, uid, packageName, active); } } @@ -378,11 +400,11 @@ public class AppOpsControllerImpl implements AppOpsController, alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null; } if (!alsoActive) { - mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true)); + notifySuscribers(code, uid, packageName, true); } } - private void notifySuscribers(int code, int uid, String packageName, boolean active) { + private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) { if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) { if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName); for (Callback cb: mCallbacksByCode.get(code)) { @@ -408,6 +430,61 @@ public class AppOpsControllerImpl implements AppOpsController, } + private boolean isAnyRecordingPausedLocked(int uid) { + List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid); + if (configs == null) return false; + int configsNum = configs.size(); + for (int i = 0; i < configsNum; i++) { + AudioRecordingConfiguration config = configs.get(i); + if (config.isClientSilenced()) return true; + } + return false; + } + + private void updateRecordingPausedStatus() { + synchronized (mActiveItems) { + int size = mActiveItems.size(); + for (int i = 0; i < size; i++) { + AppOpItem item = mActiveItems.get(i); + if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) { + boolean paused = isAnyRecordingPausedLocked(item.getUid()); + if (item.isSilenced() != paused) { + item.setSilenced(paused); + notifySuscribers( + item.getCode(), + item.getUid(), + item.getPackageName(), + !item.isSilenced() + ); + } + } + } + } + } + + private AudioManager.AudioRecordingCallback mAudioRecordingCallback = + new AudioManager.AudioRecordingCallback() { + @Override + public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) { + synchronized (mActiveItems) { + mRecordingsByUid.clear(); + final int recordingsCount = configs.size(); + for (int i = 0; i < recordingsCount; i++) { + AudioRecordingConfiguration recording = configs.get(i); + + ArrayList<AudioRecordingConfiguration> recordings = mRecordingsByUid.get( + recording.getClientUid()); + if (recordings == null) { + recordings = new ArrayList<>(); + mRecordingsByUid.put(recording.getClientUid(), recordings); + } + recordings.add(recording); + } + } + updateRecordingPausedStatus(); + } + }; + protected class H extends Handler { H(Looper looper) { super(looper); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 8a51c8515852..e77e499223aa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -44,6 +44,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert @@ -97,6 +98,7 @@ class MediaDataManager( dumpManager: DumpManager, mediaTimeoutListener: MediaTimeoutListener, mediaResumeListener: MediaResumeListener, + private val activityStarter: ActivityStarter, private var useMediaResumption: Boolean, private val useQsMediaPlayer: Boolean ) : Dumpable { @@ -113,10 +115,11 @@ class MediaDataManager( dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, mediaTimeoutListener: MediaTimeoutListener, - mediaResumeListener: MediaResumeListener + mediaResumeListener: MediaResumeListener, + activityStarter: ActivityStarter ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener, - Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) + activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context)) private val appChangeReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -403,10 +406,13 @@ class MediaDataManager( } val runnable = if (action.actionIntent != null) { Runnable { - try { - action.actionIntent.send() - } catch (e: PendingIntent.CanceledException) { - Log.d(TAG, "Intent canceled", e) + if (action.isAuthenticationRequired()) { + activityStarter.dismissKeyguardThenExecute ({ + var result = sendPendingIntent(action.actionIntent) + result + }, {}, true) + } else { + sendPendingIntent(action.actionIntent) } } } else { @@ -449,6 +455,15 @@ class MediaDataManager( return null } + private fun sendPendingIntent(intent: PendingIntent): Boolean { + return try { + intent.send() + true + } catch (e: PendingIntent.CanceledException) { + Log.d(TAG, "Intent canceled", e) + false + } + } /** * Load a bitmap from a URI * @param uri the uri to load diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 01670285c0f4..f1c8b0cf0e42 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -186,20 +186,23 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private class PipManagerPinnedStackListener extends PinnedStackListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - if (mState == STATE_PIP) { - if (mImeVisible != imeVisible) { - if (imeVisible) { - // Save the IME height adjustment, and offset to not occlude the IME - mPipBounds.offset(0, -imeHeight); - mImeHeightAdjustment = imeHeight; - } else { - // Apply the inverse adjustment when the IME is hidden - mPipBounds.offset(0, mImeHeightAdjustment); + mHandler.post(() -> { + mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight); + if (mState == STATE_PIP) { + if (mImeVisible != imeVisible) { + if (imeVisible) { + // Save the IME height adjustment, and offset to not occlude the IME + mPipBounds.offset(0, -imeHeight); + mImeHeightAdjustment = imeHeight; + } else { + // Apply the inverse adjustment when the IME is hidden + mPipBounds.offset(0, mImeHeightAdjustment); + } + mImeVisible = imeVisible; + resizePinnedStack(STATE_PIP); } - mImeVisible = imeVisible; - resizePinnedStack(STATE_PIP); } - } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 6b023c07b1a6..95867957f648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -161,7 +161,9 @@ public class NotificationRemoteInputManager implements Dumpable { ActivityManager.getService().resumeAppSwitches(); } catch (RemoteException e) { } - return mCallback.handleRemoteViewClick(view, pendingIntent, () -> { + Notification.Action action = getActionFromView(view, entry, pendingIntent); + return mCallback.handleRemoteViewClick(view, pendingIntent, + action == null ? false : action.isAuthenticationRequired(), () -> { Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); options.second.setLaunchWindowingMode( WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); @@ -170,47 +172,56 @@ public class NotificationRemoteInputManager implements Dumpable { }); } - private void logActionClick( - View view, - NotificationEntry entry, - PendingIntent actionIntent) { + private @Nullable Notification.Action getActionFromView(View view, + NotificationEntry entry, PendingIntent actionIntent) { Integer actionIndex = (Integer) view.getTag(com.android.internal.R.id.notification_action_index_tag); if (actionIndex == null) { - // Custom action button, not logging. - return; + return null; } - ViewParent parent = view.getParent(); if (entry == null) { Log.w(TAG, "Couldn't determine notification for click."); - return; - } - StatusBarNotification statusBarNotification = entry.getSbn(); - String key = statusBarNotification.getKey(); - int buttonIndex = -1; - // If this is a default template, determine the index of the button. - if (view.getId() == com.android.internal.R.id.action0 && - parent != null && parent instanceof ViewGroup) { - ViewGroup actionGroup = (ViewGroup) parent; - buttonIndex = actionGroup.indexOfChild(view); + return null; } - final int count = mEntryManager.getActiveNotificationsCount(); - final int rank = mEntryManager - .getActiveNotificationUnfiltered(key).getRanking().getRank(); // Notification may be updated before this function is executed, and thus play safe // here and verify that the action object is still the one that where the click happens. + StatusBarNotification statusBarNotification = entry.getSbn(); Notification.Action[] actions = statusBarNotification.getNotification().actions; if (actions == null || actionIndex >= actions.length) { Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid"); - return; + return null ; } final Notification.Action action = statusBarNotification.getNotification().actions[actionIndex]; if (!Objects.equals(action.actionIntent, actionIntent)) { Log.w(TAG, "actionIntent does not match"); + return null; + } + return action; + } + + private void logActionClick( + View view, + NotificationEntry entry, + PendingIntent actionIntent) { + Notification.Action action = getActionFromView(view, entry, actionIntent); + if (action == null) { return; } + ViewParent parent = view.getParent(); + String key = entry.getSbn().getKey(); + int buttonIndex = -1; + // If this is a default template, determine the index of the button. + if (view.getId() == com.android.internal.R.id.action0 && + parent != null && parent instanceof ViewGroup) { + ViewGroup actionGroup = (ViewGroup) parent; + buttonIndex = actionGroup.indexOfChild(view); + } + final int count = mEntryManager.getActiveNotificationsCount(); + final int rank = mEntryManager + .getActiveNotificationUnfiltered(key).getRanking().getRank(); + NotificationVisibility.NotificationLocation location = NotificationLogger.getNotificationLocation( mEntryManager.getActiveNotificationUnfiltered(key)); @@ -813,11 +824,12 @@ public class NotificationRemoteInputManager implements Dumpable { * * @param view * @param pendingIntent + * @param appRequestedAuth * @param defaultHandler * @return true iff the click was handled */ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, - ClickHandler defaultHandler); + boolean appRequestedAuth, ClickHandler defaultHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 36f8bcdc9123..8f3033edecbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.phone.NotificationIconContainer; @@ -69,7 +69,7 @@ public class NotificationShelf extends ActivatableNotificationView implements private float mHiddenShelfIconSize; private int mStatusBarHeight; private AmbientState mAmbientState; - private NotificationStackScrollLayout mHostLayout; + private NotificationStackScrollLayoutController mHostLayoutController; private int mMaxLayoutHeight; private int mPaddingBetweenElements; private int mNotGoneIndex; @@ -114,9 +114,10 @@ public class NotificationShelf extends ActivatableNotificationView implements initDimens(); } - public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) { + public void bind(AmbientState ambientState, + NotificationStackScrollLayoutController hostLayoutController) { mAmbientState = ambientState; - mHostLayout = hostLayout; + mHostLayoutController = hostLayoutController; } private void initDimens() { @@ -248,8 +249,8 @@ public class NotificationShelf extends ActivatableNotificationView implements float firstElementRoundness = 0.0f; ActivatableNotificationView previousAnv = null; - for (int i = 0; i < mHostLayout.getChildCount(); i++) { - ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); + for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { + ExpandableView child = (ExpandableView) mHostLayoutController.getChildAt(i); if (!child.needsClippingToShelf() || child.getVisibility() == GONE) { continue; @@ -354,8 +355,8 @@ public class NotificationShelf extends ActivatableNotificationView implements mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); - for (int i = 0; i < mHostLayout.getChildCount(); i++) { - View child = mHostLayout.getChildAt(i); + for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { + View child = mHostLayoutController.getChildAt(i); if (!(child instanceof ExpandableNotificationRow) || child.getVisibility() == GONE) { continue; @@ -378,8 +379,8 @@ public class NotificationShelf extends ActivatableNotificationView implements * swipes quickly. */ private void clipTransientViews() { - for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) { - View transientView = mHostLayout.getTransientView(i); + for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) { + View transientView = mHostLayoutController.getTransientView(i); if (transientView instanceof ExpandableView) { ExpandableView transientExpandableView = (ExpandableView) transientView; updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1); @@ -618,7 +619,7 @@ public class NotificationShelf extends ActivatableNotificationView implements // We need to persist this, since after the expansion, the behavior should still be the // same. float position = mAmbientState.getIntrinsicPadding() - + mHostLayout.getPositionInLinearLayout(view); + + mHostLayoutController.getPositionInLinearLayout(view); int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight(); if (position < maxShelfStart && position + view.getIntrinsicHeight() >= maxShelfStart && view.getTranslationY() < position) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java index 9f8f8447d72c..77abcfa848ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java @@ -21,7 +21,7 @@ import android.view.View; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.AmbientState; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter; @@ -90,8 +90,8 @@ public class NotificationShelfController { } public void bind(AmbientState ambientState, - NotificationStackScrollLayout notificationStackScrollLayout) { - mView.bind(ambientState, notificationStackScrollLayout); + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { + mView.bind(ambientState, notificationStackScrollLayoutController); mAmbientState = ambientState; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 7b2585324b9f..ff13c4e90bba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ShadeController @@ -93,7 +93,7 @@ constructor( private set private val mTouchSlop: Float private lateinit var expansionCallback: ExpansionCallback - private lateinit var stackScroller: NotificationStackScrollLayout + private lateinit var stackScrollerController: NotificationStackScrollLayoutController private val mTemp2 = IntArray(2) private var mDraggedFarEnough: Boolean = false private var mStartingChild: ExpandableView? = null @@ -315,23 +315,23 @@ constructor( private fun findView(x: Float, y: Float): ExpandableView? { var totalX = x var totalY = y - stackScroller.getLocationOnScreen(mTemp2) + stackScrollerController.getLocationOnScreen(mTemp2) totalX += mTemp2[0].toFloat() totalY += mTemp2[1].toFloat() - val childAtRawPosition = stackScroller.getChildAtRawPosition(totalX, totalY) + val childAtRawPosition = stackScrollerController.getChildAtRawPosition(totalX, totalY) return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) { childAtRawPosition } else null } fun setUp( - stackScroller: NotificationStackScrollLayout, + stackScrollerController: NotificationStackScrollLayoutController, expansionCallback: ExpansionCallback, shadeController: ShadeController ) { this.expansionCallback = expansionCallback this.shadeController = shadeController - this.stackScroller = stackScroller + this.stackScrollerController = stackScrollerController } fun setPulsing(pulsing: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index f982cf05de97..0469176255c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -22,7 +22,7 @@ import com.android.systemui.Interpolators import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -33,6 +33,7 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import javax.inject.Inject import javax.inject.Singleton +import kotlin.math.min @Singleton class NotificationWakeUpCoordinator @Inject constructor( @@ -53,7 +54,7 @@ class NotificationWakeUpCoordinator @Inject constructor( return coordinator.mLinearVisibilityAmount } } - private lateinit var mStackScroller: NotificationStackScrollLayout + private lateinit var mStackScrollerController: NotificationStackScrollLayoutController private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE private var mLinearDozeAmount: Float = 0.0f @@ -79,7 +80,7 @@ class NotificationWakeUpCoordinator @Inject constructor( if (mNotificationsVisible && !mNotificationsVisibleForExpansion && !bypassController.bypassEnabled) { // We're waking up while pulsing, let's make sure the animation looks nice - mStackScroller.wakeUpFromPulse() + mStackScrollerController.wakeUpFromPulse() } if (bypassController.bypassEnabled && !mNotificationsVisible) { // Let's make sure our huns become visible once we are waking up in case @@ -156,10 +157,10 @@ class NotificationWakeUpCoordinator @Inject constructor( }) } - fun setStackScroller(stackScroller: NotificationStackScrollLayout) { - mStackScroller = stackScroller - pulseExpanding = stackScroller.isPulseExpanding - stackScroller.setOnPulseHeightChangedListener { + fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) { + mStackScrollerController = stackScrollerController + pulseExpanding = stackScrollerController.isPulseExpanding + stackScrollerController.setOnPulseHeightChangedListener { val nowExpanding = isPulseExpanding() val changed = nowExpanding != pulseExpanding pulseExpanding = nowExpanding @@ -169,7 +170,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } } - fun isPulseExpanding(): Boolean = mStackScroller.isPulseExpanding + fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding /** * @param visible should notifications be visible @@ -249,7 +250,7 @@ class NotificationWakeUpCoordinator @Inject constructor( val changed = linear != mLinearDozeAmount mLinearDozeAmount = linear mDozeAmount = eased - mStackScroller.setDozeAmount(mDozeAmount) + mStackScrollerController.setDozeAmount(mDozeAmount) updateHideAmount() if (changed && linear == 0.0f) { setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) @@ -330,18 +331,18 @@ class NotificationWakeUpCoordinator @Inject constructor( } fun getWakeUpHeight(): Float { - return mStackScroller.wakeUpHeight + return mStackScrollerController.wakeUpHeight } private fun updateHideAmount() { - val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) - val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) - mStackScroller.setHideAmount(linearAmount, amount) + val linearAmount = min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) + val amount = min(1.0f - mVisibilityAmount, mDozeAmount) + mStackScrollerController.setHideAmount(linearAmount, amount) notificationsFullyHidden = linearAmount == 1.0f } private fun notifyAnimationStart(awake: Boolean) { - mStackScroller.notifyHideAnimationStart(!awake) + mStackScrollerController.notifyHideAnimationStart(!awake) } override fun onDozingChanged(isDozing: Boolean) { @@ -355,7 +356,7 @@ class NotificationWakeUpCoordinator @Inject constructor( * from a pulse and determines how much the notifications are expanded. */ fun setPulseHeight(height: Float): Float { - val overflow = mStackScroller.setPulseHeight(height) + val overflow = mStackScrollerController.setPulseHeight(height) // no overflow for the bypass experience return if (bypassController.bypassEnabled) 0.0f else overflow } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 57c1a16a259d..2ff67abd6377 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.stack; import static android.service.notification.NotificationStats.DISMISSAL_SHADE; import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING; @@ -210,7 +209,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private final Paint mBackgroundPaint = new Paint(); private final boolean mShouldDrawNotificationBackground; private boolean mHighPriorityBeforeSpeedBump; - private final boolean mAllowLongPress; private boolean mDismissRtl; private float mExpandedHeight; @@ -539,6 +537,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mGapHeight; private int mWaterfallTopInset; + private NotificationStackScrollLayoutController mController; private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (colorExtractor, which) -> { @@ -550,7 +549,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, SysuiStatusBarStateController statusBarStateController, @@ -573,8 +571,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd super(context, attrs, 0, 0); Resources res = getResources(); - mAllowLongPress = allowLongPress; - mRoundnessManager = notificationRoundnessManager; mLockscreenUserManager = notificationLockscreenUserManager; @@ -715,9 +711,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd inflateEmptyShadeView(); inflateFooterView(); mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation); - if (mAllowLongPress) { - setLongPressListener(mNotificationGutsManager::openGuts); - } } /** @@ -5430,7 +5423,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd addView(mShelf, index); mAmbientState.setShelf(mShelf); mStateAnimator.setShelf(mShelf); - notificationShelfController.bind(mAmbientState, this); + notificationShelfController.bind(mAmbientState, mController); if (ANCHOR_SCROLLING) { mScrollAnchorView = mShelf; } @@ -5904,6 +5897,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return MathUtils.smoothStep(0, totalDistance, dragDownAmount); } + public void setController( + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { + mController = notificationStackScrollLayoutController; + } + + public NotificationStackScrollLayoutController getController() { + return mController; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java new file mode 100644 index 000000000000..8150593e69fd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack; + +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; + +import android.graphics.PointF; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.FrameLayout; + +import com.android.systemui.statusbar.NotificationShelfController; +import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ExpandableView; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.NotificationPanelViewController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; + +import java.util.function.BiConsumer; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Controller for {@link NotificationStackScrollLayout}. + */ +@StatusBarComponent.StatusBarScope +public class NotificationStackScrollLayoutController { + private final boolean mAllowLongPress; + private final NotificationGutsManager mNotificationGutsManager; + private NotificationStackScrollLayout mView; + + @Inject + public NotificationStackScrollLayoutController( + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, + NotificationGutsManager notificationGutsManager) { + mAllowLongPress = allowLongPress; + mNotificationGutsManager = notificationGutsManager; + } + + public void attach(NotificationStackScrollLayout view) { + mView = view; + mView.setController(this); + + if (mAllowLongPress) { + mView.setLongPressListener(mNotificationGutsManager::openGuts); + } + } + + public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { + mView.addOnExpandedHeightChangedListener(listener); + } + + public void removeOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) { + mView.removeOnExpandedHeightChangedListener(listener); + } + + public void addOnLayoutChangeListener(View.OnLayoutChangeListener listener) { + mView.addOnLayoutChangeListener(listener); + } + + public void removeOnLayoutChangeListener(View.OnLayoutChangeListener listener) { + mView.removeOnLayoutChangeListener(listener); + } + + public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) { + mView.setHeadsUpAppearanceController(controller); + } + + public void requestLayout() { + mView.requestLayout(); + } + + public Display getDisplay() { + return mView.getDisplay(); + } + + public WindowInsets getRootWindowInsets() { + return mView.getRootWindowInsets(); + } + + public int getRight() { + return mView.getRight(); + } + + public boolean isLayoutRtl() { + return mView.isLayoutRtl(); + } + + public float getLeft() { + return mView.getLeft(); + } + + public float getTranslationX() { + return mView.getTranslationX(); + } + + public void setOnHeightChangedListener( + ExpandableView.OnHeightChangedListener listener) { + mView.setOnHeightChangedListener(listener); + } + + public void setOverscrollTopChangedListener( + NotificationStackScrollLayout.OnOverscrollTopChangedListener listener) { + mView.setOverscrollTopChangedListener(listener); + } + + public void setOnEmptySpaceClickListener( + NotificationStackScrollLayout.OnEmptySpaceClickListener listener) { + mView.setOnEmptySpaceClickListener(listener); + } + + public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) { + mView.setTrackingHeadsUp(expandableNotificationRow); + } + + public void wakeUpFromPulse() { + mView.wakeUpFromPulse(); + } + + public boolean isPulseExpanding() { + return mView.isPulseExpanding(); + } + + public void setOnPulseHeightChangedListener(Runnable listener) { + mView.setOnPulseHeightChangedListener(listener); + } + + public void setDozeAmount(float amount) { + mView.setDozeAmount(amount); + } + + public float getWakeUpHeight() { + return mView.getWakeUpHeight(); + } + + public void setHideAmount(float linearAmount, float amount) { + mView.setHideAmount(linearAmount, amount); + } + + public void notifyHideAnimationStart(boolean hide) { + mView.notifyHideAnimationStart(hide); + } + + public float setPulseHeight(float height) { + return mView.setPulseHeight(height); + } + + public void getLocationOnScreen(int[] outLocation) { + mView.getLocationOnScreen(outLocation); + } + + public ExpandableView getChildAtRawPosition(float x, float y) { + return mView.getChildAtRawPosition(x, y); + } + + public FrameLayout.LayoutParams getLayoutParams() { + return (FrameLayout.LayoutParams) mView.getLayoutParams(); + } + + public void setLayoutParams(FrameLayout.LayoutParams lp) { + mView.setLayoutParams(lp); + } + + public void setIsFullWidth(boolean isFullWidth) { + mView.setIsFullWidth(isFullWidth); + } + + public boolean isAddOrRemoveAnimationPending() { + return mView.isAddOrRemoveAnimationPending(); + } + + public int getVisibleNotificationCount() { + return mView.getVisibleNotificationCount(); + } + + public int getIntrinsicContentHeight() { + return mView.getIntrinsicContentHeight(); + } + + public void setIntrinsicPadding(int intrinsicPadding) { + mView.setIntrinsicPadding(intrinsicPadding); + } + + public int getHeight() { + return mView.getHeight(); + } + + public int getChildCount() { + return mView.getChildCount(); + } + + public ExpandableView getChildAt(int i) { + return (ExpandableView) mView.getChildAt(i); + } + + public void goToFullShade(long delay) { + mView.goToFullShade(delay); + } + + public void setOverScrollAmount(float amount, boolean onTop, boolean animate, + boolean cancelAnimators) { + mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators); + } + + public void setOverScrollAmount(float amount, boolean onTop, boolean animate) { + mView.setOverScrollAmount(amount, onTop, animate); + } + + public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) { + mView.setOverScrolledPixels(numPixels, onTop, animate); + } + + public void resetScrollPosition() { + mView.resetScrollPosition(); + } + + public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { + mView.setShouldShowShelfOnly(shouldShowShelfOnly); + } + + public void cancelLongPress() { + mView.cancelLongPress(); + } + + public float getX() { + return mView.getX(); + } + + public boolean isBelowLastNotification(float x, float y) { + return mView.isBelowLastNotification(x, y); + } + + public float getWidth() { + return mView.getWidth(); + } + + public float getOpeningHeight() { + return mView.getOpeningHeight(); + } + + public float getBottomMostNotificationBottom() { + return mView.getBottomMostNotificationBottom(); + } + + public void checkSnoozeLeavebehind() { + mView.checkSnoozeLeavebehind(); + } + + public void setQsExpanded(boolean expanded) { + mView.setQsExpanded(expanded); + } + + public void setScrollingEnabled(boolean enabled) { + mView.setScrollingEnabled(enabled); + } + + public void setQsExpansionFraction(float expansionFraction) { + mView.setQsExpansionFraction(expansionFraction); + } + + public float calculateAppearFractionBypass() { + return mView.calculateAppearFractionBypass(); + } + + public void updateTopPadding(float qsHeight, boolean animate) { + mView.updateTopPadding(qsHeight, animate); + } + + public void resetCheckSnoozeLeavebehind() { + mView.resetCheckSnoozeLeavebehind(); + } + + public boolean isScrolledToBottom() { + return mView.isScrolledToBottom(); + } + + public int getNotGoneChildCount() { + return mView.getNotGoneChildCount(); + } + + public float getIntrinsicPadding() { + return mView.getIntrinsicPadding(); + } + + public float getLayoutMinHeight() { + return mView.getLayoutMinHeight(); + } + + public int getEmptyBottomMargin() { + return mView.getEmptyBottomMargin(); + } + + public float getTopPaddingOverflow() { + return mView.getTopPaddingOverflow(); + } + + public int getTopPadding() { + return mView.getTopPadding(); + } + + public float getEmptyShadeViewHeight() { + return mView.getEmptyShadeViewHeight(); + } + + public void setAlpha(float alpha) { + mView.setAlpha(alpha); + } + + public float getCurrentOverScrollAmount(boolean top) { + return mView.getCurrentOverScrollAmount(top); + } + + public float getCurrentOverScrolledPixels(boolean top) { + return mView.getCurrentOverScrolledPixels(top); + } + + public float calculateAppearFraction(float height) { + return mView.calculateAppearFraction(height); + } + + public void onExpansionStarted() { + mView.onExpansionStarted(); + } + + public void onExpansionStopped() { + mView.onExpansionStopped(); + } + + public void onPanelTrackingStarted() { + mView.onPanelTrackingStarted(); + } + + public void onPanelTrackingStopped() { + mView.onPanelTrackingStopped(); + } + + public void setHeadsUpBoundaries(int height, int bottomBarHeight) { + mView.setHeadsUpBoundaries(height, bottomBarHeight); + } + + public void setUnlockHintRunning(boolean running) { + mView.setUnlockHintRunning(running); + } + + public float getPeekHeight() { + return mView.getPeekHeight(); + } + + public boolean isFooterViewNotGone() { + return mView.isFooterViewNotGone(); + } + + public boolean isFooterViewContentVisible() { + return mView.isFooterViewContentVisible(); + } + + public int getFooterViewHeightWithPadding() { + return mView.getFooterViewHeightWithPadding(); + } + + public void updateEmptyShadeView(boolean visible) { + mView.updateEmptyShadeView(visible); + } + + public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { + mView.setHeadsUpAnimatingAway(headsUpAnimatingAway); + } + + public HeadsUpTouchHelper.Callback getHeadsUpCallback() { + return mView.getHeadsUpCallback(); + } + + public void forceNoOverlappingRendering(boolean force) { + mView.forceNoOverlappingRendering(force); + } + + public void setTranslationX(float translation) { + mView.setTranslationX(translation); + } + + public void setExpandingVelocity(float velocity) { + mView.setExpandingVelocity(velocity); + } + + public void setExpandedHeight(float expandedHeight) { + mView.setExpandedHeight(expandedHeight); + } + + public void setQsContainer(ViewGroup view) { + mView.setQsContainer(view); + } + + public void setAnimationsEnabled(boolean enabled) { + mView.setAnimationsEnabled(enabled); + } + + public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { + mView.setDozing(dozing, animate, wakeUpTouchLocation); + } + + public void setPulsing(boolean pulsing, boolean animatePulse) { + mView.setPulsing(pulsing, animatePulse); + } + + public boolean hasActiveClearableNotifications( + @NotificationStackScrollLayout.SelectedRows int selection) { + return mView.hasActiveClearableNotifications(selection); + } + + public RemoteInputController.Delegate createDelegate() { + return mView.createDelegate(); + } + + public void updateSectionBoundaries(String reason) { + mView.updateSectionBoundaries(reason); + } + + public void updateSpeedBumpIndex() { + mView.updateSpeedBumpIndex(); + } + + public void updateFooter() { + mView.updateFooter(); + } + + public void updateIconAreaViews() { + mView.updateIconAreaViews(); + } + + public void onUpdateRowStates() { + mView.onUpdateRowStates(); + } + + public boolean hasPulsingNotifications() { + return mView.hasPulsingNotifications(); + } + + public ActivatableNotificationView getActivatedChild() { + return mView.getActivatedChild(); + } + + public void setActivatedChild(ActivatableNotificationView view) { + mView.setActivatedChild(view); + } + + public void runAfterAnimationFinished(Runnable r) { + mView.runAfterAnimationFinished(r); + } + + public void setNotificationPanelController( + NotificationPanelViewController notificationPanelViewController) { + mView.setNotificationPanelController(notificationPanelViewController); + } + + public void setIconAreaController(NotificationIconAreaController controller) { + mView.setIconAreaController(controller); + } + + public void setStatusBar(StatusBar statusBar) { + mView.setStatusBar(statusBar); + } + + public void setGroupManager(NotificationGroupManager groupManager) { + mView.setGroupManager(groupManager); + } + + public void setShelfController(NotificationShelfController notificationShelfController) { + mView.setShelfController(notificationShelfController); + } + + public void setScrimController(ScrimController scrimController) { + mView.setScrimController(scrimController); + } + + public ExpandableView getFirstChildNotGone() { + return mView.getFirstChildNotGone(); + } + + public void setInHeadsUpPinnedMode(boolean inPinnedMode) { + mView.setInHeadsUpPinnedMode(inPinnedMode); + } + + public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { + mView.generateHeadsUpAnimation(entry, isHeadsUp); + } + + public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) { + mView.generateHeadsUpAnimation(row, isHeadsUp); + } + + public void setMaxTopPadding(int padding) { + mView.setMaxTopPadding(padding); + } + + public int getTransientViewCount() { + return mView.getTransientViewCount(); + } + + public View getTransientView(int i) { + return mView.getTransientView(i); + } + + public int getPositionInLinearLayout(ExpandableView row) { + return mView.getPositionInLinearLayout(row); + } + + public NotificationStackScrollLayout getView() { + return mView; + } + + public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) { + return mView.calculateGapHeight(previousView, child, count); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 51c02c9f93ab..8cdaa63994e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -52,7 +52,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, public static final int CONTENT_FADE_DELAY = 100; private final NotificationIconAreaController mNotificationIconAreaController; private final HeadsUpManagerPhone mHeadsUpManager; - private final NotificationStackScrollLayout mStackScroller; + private final NotificationStackScrollLayoutController mStackScrollerController; private final HeadsUpStatusBarView mHeadsUpStatusBarView; private final View mCenteredIconView; private final View mClockView; @@ -93,7 +93,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, HeadsUpManagerPhone headsUpManager, - View notificationShadeView, + NotificationStackScrollLayoutController notificationStackScrollLayoutController, SysuiStatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, KeyguardStateController keyguardStateController, @@ -101,10 +101,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, NotificationPanelViewController notificationPanelViewController, View statusBarView) { this(notificationIconAreaController, headsUpManager, statusBarStateController, keyguardBypassController, wakeUpCoordinator, keyguardStateController, - commandQueue, - statusBarView.findViewById(R.id.heads_up_status_bar_view), - notificationShadeView.findViewById(R.id.notification_stack_scroller), + commandQueue, notificationStackScrollLayoutController, notificationPanelViewController, + statusBarView.findViewById(R.id.heads_up_status_bar_view), statusBarView.findViewById(R.id.clock), statusBarView.findViewById(R.id.operator_name_frame), statusBarView.findViewById(R.id.centered_icon_area)); @@ -119,9 +118,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardStateController keyguardStateController, CommandQueue commandQueue, - HeadsUpStatusBarView headsUpStatusBarView, - NotificationStackScrollLayout stackScroller, + NotificationStackScrollLayoutController stackScrollerController, NotificationPanelViewController notificationPanelViewController, + HeadsUpStatusBarView headsUpStatusBarView, View clockView, View operatorNameView, View centeredIconView) { @@ -132,14 +131,14 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mCenteredIconView = centeredIconView; headsUpStatusBarView.setOnDrawingRectChangedListener( () -> updateIsolatedIconLocation(true /* requireUpdate */)); - mStackScroller = stackScroller; + mStackScrollerController = stackScrollerController; mNotificationPanelViewController = notificationPanelViewController; notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp); notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation); notificationPanelViewController.setHeadsUpAppearanceController(this); - mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight); - mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); - mStackScroller.setHeadsUpAppearanceController(this); + mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight); + mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); + mStackScrollerController.setHeadsUpAppearanceController(this); mClockView = clockView; mOperatorNameView = operatorNameView; mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); @@ -153,7 +152,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, updateTopEntry(); // trigger scroller to notify the latest panel translation - mStackScroller.requestLayout(); + mStackScrollerController.requestLayout(); } mHeadsUpStatusBarView.removeOnLayoutChangeListener(this); } @@ -174,8 +173,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation); mNotificationPanelViewController.setHeadsUpAppearanceController(null); - mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight); - mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); + mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight); + mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); mDarkIconDispatcher.removeDarkReceiver(this); } @@ -219,12 +218,12 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } int realDisplaySize = 0; - if (mStackScroller.getDisplay() != null) { - mStackScroller.getDisplay().getRealSize(mPoint); + if (mStackScrollerController.getDisplay() != null) { + mStackScrollerController.getDisplay().getRealSize(mPoint); realDisplaySize = mPoint.x; } - WindowInsets windowInset = mStackScroller.getRootWindowInsets(); + WindowInsets windowInset = mStackScrollerController.getRootWindowInsets(); DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null; int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0; int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0; @@ -233,17 +232,17 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, int leftInset = Math.max(sysWinLeft, cutoutLeft); int rightInset = Math.max(sysWinRight, cutoutRight); - return leftInset + mStackScroller.getRight() + rightInset - realDisplaySize; + return leftInset + mStackScrollerController.getRight() + rightInset - realDisplaySize; } public void updatePanelTranslation() { float newTranslation; - if (mStackScroller.isLayoutRtl()) { + if (mStackScrollerController.isLayoutRtl()) { newTranslation = getRtlTranslation(); } else { - newTranslation = mStackScroller.getLeft(); + newTranslation = mStackScrollerController.getLeft(); } - newTranslation += mStackScroller.getTranslationX(); + newTranslation += mStackScrollerController.getTranslationX(); mHeadsUpStatusBarView.setPanelTranslation(newTranslation); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 6946346eee2f..70fb71a1d2f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -106,6 +106,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; @@ -175,6 +176,7 @@ public class NotificationPanelViewController extends PanelViewController { private final ZenModeController mZenModeController; private final ConfigurationController mConfigurationController; private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is // changed. @@ -265,7 +267,6 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardStatusView mKeyguardStatusView; private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; - private NotificationStackScrollLayout mNotificationStackScroller; private boolean mAnimateNextPositionUpdate; private int mTrackingPointer; @@ -500,7 +501,8 @@ public class NotificationPanelViewController extends PanelViewController { MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider) { + Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider, + NotificationStackScrollLayoutController notificationStackScrollLayoutController) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); @@ -513,6 +515,7 @@ public class NotificationPanelViewController extends PanelViewController { mMediaHierarchyManager = mediaHierarchyManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider; + mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mView.setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; @@ -592,21 +595,26 @@ public class NotificationPanelViewController extends PanelViewController { keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); - mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller); - mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); - mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener); - mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener); - addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp); + NotificationStackScrollLayout stackScrollLayout = mView.findViewById( + R.id.notification_stack_scroller); + mNotificationStackScrollLayoutController.attach(stackScrollLayout); + mNotificationStackScrollLayoutController.setOnHeightChangedListener( + mOnHeightChangedListener); + mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( + mOnOverscrollTopChangedListener); + mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( + mOnEmptySpaceClickListener); + addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); - mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); + mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); mQsFrame = mView.findViewById(R.id.qs_frame); mPulseExpansionHandler.setUp( - mNotificationStackScroller, mExpansionCallback, mShadeController); + mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController); mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { @Override public void onFullyHiddenChanged(boolean isFullyHidden) { @@ -690,11 +698,11 @@ public class NotificationPanelViewController extends PanelViewController { } int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); - lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams(); + lp = mNotificationStackScrollLayoutController.getLayoutParams(); if (lp.width != panelWidth || lp.gravity != panelGravity) { lp.width = panelWidth; lp.gravity = panelGravity; - mNotificationStackScroller.setLayoutParams(lp); + mNotificationStackScrollLayoutController.setLayoutParams(lp); } } @@ -772,7 +780,7 @@ public class NotificationPanelViewController extends PanelViewController { private void setIsFullWidth(boolean isFullWidth) { mIsFullWidth = isFullWidth; - mNotificationStackScroller.setIsFullWidth(isFullWidth); + mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth); } private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { @@ -806,7 +814,7 @@ public class NotificationPanelViewController extends PanelViewController { * showing. */ private void positionClockAndNotifications() { - boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending(); + boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = animate || mAnimateNextPositionUpdate; int stackScrollerPadding; if (mBarState != StatusBarState.KEYGUARD) { @@ -816,12 +824,12 @@ public class NotificationPanelViewController extends PanelViewController { int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); - final boolean - hasVisibleNotifications = - !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0; + final boolean hasVisibleNotifications = !bypassEnabled + && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0; mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, - mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(), + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), + getExpandedFraction(), totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, @@ -835,7 +843,7 @@ public class NotificationPanelViewController extends PanelViewController { updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; } - mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); + mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); mStackScrollerMeasuringPass++; @@ -867,29 +875,44 @@ public class NotificationPanelViewController extends PanelViewController { : shelf.getIntrinsicHeight() + notificationPadding; float availableSpace = - mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max( - mIndicationBottomPadding, mAmbientIndicationBottomPadding) + mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize + - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding) - mKeyguardStatusView.getLogoutButtonHeight(); int count = 0; ExpandableView previousView = null; - for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { - ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i); + for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { + ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); + if (!(child instanceof ExpandableNotificationRow)) { + continue; + } + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + boolean + suppressedSummary = + mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( + row.getEntry().getSbn()); + if (suppressedSummary) { + continue; + } if (!canShowViewOnLockscreen(child)) { continue; } + if (row.isRemoved()) { + continue; + } availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); availableSpace -= count == 0 ? 0 : notificationPadding; - availableSpace -= mNotificationStackScroller.calculateGapHeight(previousView, child, - count); + availableSpace -= mNotificationStackScrollLayoutController + .calculateGapHeight(previousView, child, count); previousView = child; if (availableSpace >= 0 && count < maximum) { count++; } else if (availableSpace > -shelfSize) { // if we are exactly the last view, then we can show us still! - for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) { - ExpandableView view = (ExpandableView) mNotificationStackScroller.getChildAt(j); - if (view instanceof ExpandableNotificationRow && - canShowViewOnLockscreen(view)) { + int childCount = mNotificationStackScrollLayoutController.getChildCount(); + for (int j = i + 1; j < childCount; j++) { + ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); + if (view instanceof ExpandableNotificationRow + && canShowViewOnLockscreen(view)) { return count; } } @@ -954,7 +977,7 @@ public class NotificationPanelViewController extends PanelViewController { } public void animateToFullShade(long delay) { - mNotificationStackScroller.goToFullShade(delay); + mNotificationStackScrollLayoutController.goToFullShade(delay); mView.requestLayout(); mAnimateNextPositionUpdate = true; } @@ -980,9 +1003,9 @@ public class NotificationPanelViewController extends PanelViewController { } else { closeQs(); } - mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate, + mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate, !animate /* cancelAnimators */); - mNotificationStackScroller.resetScrollPosition(); + mNotificationStackScrollLayoutController.resetScrollPosition(); } @Override @@ -993,7 +1016,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mQsExpanded) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } super.collapse(delayed, speedUpFactor); } @@ -1029,7 +1052,7 @@ public class NotificationPanelViewController extends PanelViewController { public void expandWithQs() { if (mQsExpansionEnabled) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } if (isFullyCollapsed()) { expand(true /* animate */); @@ -1090,7 +1113,7 @@ public class NotificationPanelViewController extends PanelViewController { onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; mQsTracking = true; - mNotificationStackScroller.cancelLongPress(); + mNotificationStackScrollLayoutController.cancelLongPress(); } break; case MotionEvent.ACTION_POINTER_UP: @@ -1126,7 +1149,7 @@ public class NotificationPanelViewController extends PanelViewController { mInitialHeightOnTouch = mQsExpansionHeight; mInitialTouchY = y; mInitialTouchX = x; - mNotificationStackScroller.cancelLongPress(); + mNotificationStackScrollLayoutController.cancelLongPress(); return true; } break; @@ -1146,9 +1169,11 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected boolean isInContentBounds(float x, float y) { - float stackScrollerX = mNotificationStackScroller.getX(); - return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y) - && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth(); + float stackScrollerX = mNotificationStackScrollLayoutController.getX(); + return !mNotificationStackScrollLayoutController + .isBelowLastNotification(x - stackScrollerX, y) + && stackScrollerX < x + && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth(); } private void initDownStates(MotionEvent event) { @@ -1256,7 +1281,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOpeningHeight() { - return mNotificationStackScroller.getOpeningHeight(); + return mNotificationStackScrollLayoutController.getOpeningHeight(); } @@ -1292,7 +1317,7 @@ public class NotificationPanelViewController extends PanelViewController { < mStatusBarMinHeight) { mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); requestPanelHeightUpdate(); // Normally, we start listening when the panel is expanded, but here we need to start @@ -1304,7 +1329,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean isInQsArea(float x, float y) { return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && ( - y <= mNotificationStackScroller.getBottomMostNotificationBottom() + y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom() || y <= mQs.getView().getY() + mQs.getView().getHeight()); } @@ -1494,7 +1519,7 @@ public class NotificationPanelViewController extends PanelViewController { float height = mQsExpansionHeight - overscrollAmount; setQsExpansion(height); requestPanelHeightUpdate(); - mNotificationStackScroller.checkSnoozeLeavebehind(); + mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. @@ -1667,8 +1692,8 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateQsState() { - mNotificationStackScroller.setQsExpanded(mQsExpanded); - mNotificationStackScroller.setScrollingEnabled( + mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); + mNotificationStackScrollLayoutController.setScrollingEnabled( mBarState != StatusBarState.KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); updateEmptyShadeView(); @@ -1728,7 +1753,7 @@ public class NotificationPanelViewController extends PanelViewController { float qsExpansionFraction = getQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); - mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); + mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction); } private String determineAccessibilityPaneTitle() { @@ -1787,18 +1812,18 @@ public class NotificationPanelViewController extends PanelViewController { return mClockPositionResult.stackScrollerPadding; } int collapsedPosition = mHeadsUpInset; - if (!mNotificationStackScroller.isPulseExpanding()) { + if (!mNotificationStackScrollLayoutController.isPulseExpanding()) { return collapsedPosition; } else { int expandedPosition = mClockPositionResult.stackScrollerPadding; return (int) MathUtils.lerp(collapsedPosition, expandedPosition, - mNotificationStackScroller.calculateAppearFractionBypass()); + mNotificationStackScrollLayoutController.calculateAppearFractionBypass()); } } protected void requestScrollerTopPaddingUpdate(boolean animate) { - mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); + mNotificationStackScrollLayoutController.updateTopPadding(calculateQsTopPadding(), animate); if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { // update the position of the header updateQsExpansion(); @@ -1810,7 +1835,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mQs != null) { mQs.setShowCollapsedOnKeyguard( mKeyguardShowing && mKeyguardBypassController.getBypassEnabled() - && mNotificationStackScroller.isPulseExpanding()); + && mNotificationStackScrollLayoutController.isPulseExpanding()); } } @@ -1906,7 +1931,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onAnimationEnd(Animator animation) { mAnimatingQS = false; notifyExpandingFinished(); - mNotificationStackScroller.resetCheckSnoozeLeavebehind(); + mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); mQsExpansionAnimator = null; if (onFinishRunnable != null) { onFinishRunnable.run(); @@ -1945,8 +1970,8 @@ public class NotificationPanelViewController extends PanelViewController { protected boolean canCollapsePanelOnTouch() { if (!isInSettings()) { return mBarState == StatusBarState.KEYGUARD - || mNotificationStackScroller.isScrolledToBottom() - || mIsPanelCollapseOnQQS; + || mIsPanelCollapseOnQQS + || mNotificationStackScrollLayoutController.isScrolledToBottom(); } else { return true; } @@ -1964,7 +1989,7 @@ public class NotificationPanelViewController extends PanelViewController { private int getMaxPanelHeightNonBypass() { int min = mStatusBarMinHeight; if (!(mBarState == StatusBarState.KEYGUARD) - && mNotificationStackScroller.getNotGoneChildCount() == 0) { + && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) { int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); min = Math.max(min, minHeight); } @@ -1990,7 +2015,7 @@ public class NotificationPanelViewController extends PanelViewController { int position = mClockPositionAlgorithm.getExpandedClockPosition() + mKeyguardStatusView.getHeight(); - if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { + if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) { position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; } return position; @@ -2029,8 +2054,8 @@ public class NotificationPanelViewController extends PanelViewController { // minimum QS expansion + minStackHeight float panelHeightQsCollapsed = - mNotificationStackScroller.getIntrinsicPadding() - + mNotificationStackScroller.getLayoutMinHeight(); + mNotificationStackScrollLayoutController.getIntrinsicPadding() + + mNotificationStackScrollLayoutController.getLayoutMinHeight(); float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); t = (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded @@ -2062,16 +2087,16 @@ public class NotificationPanelViewController extends PanelViewController { } private int calculatePanelHeightShade() { - int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); - int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin; - maxHeight += mNotificationStackScroller.getTopPaddingOverflow(); + int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); + int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; + maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow(); if (mBarState == StatusBarState.KEYGUARD) { int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition() + mKeyguardStatusView.getHeight() - + mNotificationStackScroller.getIntrinsicContentHeight(); + + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); return Math.max(maxHeight, minKeyguardPanelBottom); } else { return maxHeight; @@ -2081,15 +2106,16 @@ public class NotificationPanelViewController extends PanelViewController { private int calculatePanelHeightQsExpanded() { float notificationHeight = - mNotificationStackScroller.getHeight() - - mNotificationStackScroller.getEmptyBottomMargin() - - mNotificationStackScroller.getTopPadding(); + mNotificationStackScrollLayoutController.getHeight() + - mNotificationStackScrollLayoutController.getEmptyBottomMargin() + - mNotificationStackScrollLayoutController.getTopPadding(); // When only empty shade view is visible in QS collapsed state, simulate that we would have // it in expanded QS state as well so we don't run into troubles when fading the view in/out // and expanding/collapsing the whole panel from/to quick settings. - if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) { - notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight(); + if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0 + && mShowEmptyShadeView) { + notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight(); } int maxQsHeight = mQsMaxExpansionHeight; @@ -2104,12 +2130,13 @@ public class NotificationPanelViewController extends PanelViewController { float totalHeight = Math.max(maxQsHeight, mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding : 0) + notificationHeight - + mNotificationStackScroller.getTopPaddingOverflow(); - if (totalHeight > mNotificationStackScroller.getHeight()) { + + mNotificationStackScrollLayoutController.getTopPaddingOverflow(); + if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) { float fullyCollapsedHeight = - maxQsHeight + mNotificationStackScroller.getLayoutMinHeight(); - totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight()); + maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight(); + totalHeight = Math.max(fullyCollapsedHeight, + mNotificationStackScrollLayoutController.getHeight()); } return (int) totalHeight; } @@ -2124,7 +2151,7 @@ public class NotificationPanelViewController extends PanelViewController { && !mKeyguardBypassController.getBypassEnabled()) { alpha *= mClockPositionResult.clockAlpha; } - mNotificationStackScroller.setAlpha(alpha); + mNotificationStackScrollLayoutController.setAlpha(alpha); } private float getFadeoutAlpha() { @@ -2140,7 +2167,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOverExpansionAmount() { - float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); + float result = mNotificationStackScrollLayoutController + .getCurrentOverScrollAmount(true /* top */); if (isNaN(result)) { Log.wtf(TAG, "OverExpansionAmount is NaN!"); } @@ -2150,7 +2178,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOverExpansionPixels() { - return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */); + return mNotificationStackScrollLayoutController + .getCurrentOverScrolledPixels(true /* top */); } /** @@ -2167,17 +2196,19 @@ public class NotificationPanelViewController extends PanelViewController { if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { return -mQs.getQsMinExpansionHeight(); } - float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight); + float appearAmount = mNotificationStackScrollLayoutController + .calculateAppearFraction(mExpandedHeight); float startHeight = -mQsExpansionHeight; if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() - && mNotificationStackScroller.isPulseExpanding()) { + && mNotificationStackScrollLayoutController.isPulseExpanding()) { if (!mPulseExpansionHandler.isExpanding() && !mPulseExpansionHandler.getLeavingLockscreen()) { // If we aborted the expansion we need to make sure the header doesn't reappear // again after the header has animated away appearAmount = 0; } else { - appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); + appearAmount = mNotificationStackScrollLayoutController + .calculateAppearFractionBypass(); } startHeight = -mQs.getQsMinExpansionHeight(); } @@ -2266,7 +2297,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onExpandingStarted() { super.onExpandingStarted(); - mNotificationStackScroller.onExpansionStarted(); + mNotificationStackScrollLayoutController.onExpansionStarted(); mIsExpanding = true; mQsExpandedWhenExpandingStarted = mQsFullyExpanded; mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted && @@ -2284,7 +2315,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onExpandingFinished() { super.onExpandingFinished(); - mNotificationStackScroller.onExpansionStopped(); + mNotificationStackScrollLayoutController.onExpansionStopped(); mHeadsUpManager.onExpandingFinished(); mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); mIsExpanding = false; @@ -2310,7 +2341,7 @@ public class NotificationPanelViewController extends PanelViewController { setListening(true); } mQsExpandImmediate = false; - mNotificationStackScroller.setShouldShowShelfOnly(false); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false); mTwoFingerQsExpandPossible = false; notifyListenersTrackingHeadsUp(null); mExpandingFromHeadsUp = false; @@ -2342,15 +2373,16 @@ public class NotificationPanelViewController extends PanelViewController { return; } if (mBarState != StatusBarState.KEYGUARD) { - mNotificationStackScroller.setOnHeightChangedListener(null); + mNotificationStackScrollLayoutController.setOnHeightChangedListener(null); if (isPixels) { - mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */, - false /* animate */); + mNotificationStackScrollLayoutController.setOverScrolledPixels( + overExpansion, true /* onTop */, false /* animate */); } else { - mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */, - false /* animate */); + mNotificationStackScrollLayoutController.setOverScrollAmount( + overExpansion, true /* onTop */, false /* animate */); } - mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener); + mNotificationStackScrollLayoutController + .setOnHeightChangedListener(mOnHeightChangedListener); } } @@ -2360,12 +2392,12 @@ public class NotificationPanelViewController extends PanelViewController { super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; - mNotificationStackScroller.setShouldShowShelfOnly(true); + mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { mAffordanceHelper.animateHideLeftRightIcon(); } - mNotificationStackScroller.onPanelTrackingStarted(); + mNotificationStackScrollLayoutController.onPanelTrackingStarted(); } @Override @@ -2373,10 +2405,10 @@ public class NotificationPanelViewController extends PanelViewController { mFalsingManager.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { - mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */, + mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */, true /* animate */); } - mNotificationStackScroller.onPanelTrackingStopped(); + mNotificationStackScrollLayoutController.onPanelTrackingStopped(); if (expand && (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED)) { if (!mHintAnimationRunning) { @@ -2386,7 +2418,8 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateMaxHeadsUpTranslation() { - mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight); + mNotificationStackScrollLayoutController.setHeadsUpBoundaries( + getHeight(), mNavigationBarBottomHeight); } @Override @@ -2402,19 +2435,19 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onUnlockHintFinished() { super.onUnlockHintFinished(); - mNotificationStackScroller.setUnlockHintRunning(false); + mNotificationStackScrollLayoutController.setUnlockHintRunning(false); } @Override protected void onUnlockHintStarted() { super.onUnlockHintStarted(); - mNotificationStackScroller.setUnlockHintRunning(true); + mNotificationStackScrollLayoutController.setUnlockHintRunning(true); } @Override protected float getPeekHeight() { - if (mNotificationStackScroller.getNotGoneChildCount() > 0) { - return mNotificationStackScroller.getPeekHeight(); + if (mNotificationStackScrollLayoutController.getNotGoneChildCount() > 0) { + return mNotificationStackScrollLayoutController.getPeekHeight(); } else { return mQsMinExpansionHeight; } @@ -2428,7 +2461,8 @@ public class NotificationPanelViewController extends PanelViewController { } // Let's make sure we're not appearing but the animation will end below the appear. // Otherwise quick settings would jump at the end of the animation. - float fraction = mNotificationStackScroller.calculateAppearFraction(targetHeight); + float fraction = mNotificationStackScrollLayoutController + .calculateAppearFraction(targetHeight); return fraction >= 1.0f; } @@ -2440,18 +2474,19 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected boolean fullyExpandedClearAllVisible() { - return mNotificationStackScroller.isFooterViewNotGone() - && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate; + return mNotificationStackScrollLayoutController.isFooterViewNotGone() + && mNotificationStackScrollLayoutController.isScrolledToBottom() + && !mQsExpandImmediate; } @Override protected boolean isClearAllVisible() { - return mNotificationStackScroller.isFooterViewContentVisible(); + return mNotificationStackScrollLayoutController.isFooterViewContentVisible(); } @Override protected int getClearAllHeightWithPadding() { - return mNotificationStackScroller.getFooterViewHeightWithPadding(); + return mNotificationStackScrollLayoutController.getFooterViewHeightWithPadding(); } @Override @@ -2502,7 +2537,8 @@ public class NotificationPanelViewController extends PanelViewController { private void updateEmptyShadeView() { // Hide "No notifications" in QS. - mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded); + mNotificationStackScrollLayoutController.updateEmptyShadeView( + mShowEmptyShadeView && !mQsExpanded); } public void setQsScrimEnabled(boolean qsScrimEnabled) { @@ -2593,7 +2629,7 @@ public class NotificationPanelViewController extends PanelViewController { public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { mHeadsUpAnimatingAway = headsUpAnimatingAway; - mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway); + mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); updateHeadsUpVisibility(); } @@ -2605,7 +2641,7 @@ public class NotificationPanelViewController extends PanelViewController { public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { super.setHeadsUpManager(headsUpManager); mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, - mNotificationStackScroller.getHeadsUpCallback(), + mNotificationStackScrollLayoutController.getHeadsUpCallback(), NotificationPanelViewController.this); } @@ -2627,7 +2663,7 @@ public class NotificationPanelViewController extends PanelViewController { private void setClosingWithAlphaFadeout(boolean closing) { mClosingWithAlphaFadeOut = closing; - mNotificationStackScroller.forceNoOverlappingRendering(closing); + mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing); } /** @@ -2637,22 +2673,24 @@ public class NotificationPanelViewController extends PanelViewController { * @param x the x-coordinate the touch event */ protected void updateVerticalPanelPosition(float x) { - if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) { + if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) { resetHorizontalPanelPosition(); return; } - float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2; + float leftMost = mPositionMinSideMargin + + mNotificationStackScrollLayoutController.getWidth() / 2; float rightMost = mView.getWidth() - mPositionMinSideMargin - - mNotificationStackScroller.getWidth() / 2; - if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) { + - mNotificationStackScrollLayoutController.getWidth() / 2; + if (Math.abs(x - mView.getWidth() / 2) + < mNotificationStackScrollLayoutController.getWidth() / 4) { x = mView.getWidth() / 2; } x = Math.min(rightMost, Math.max(leftMost, x)); float - center = - mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2; + center = mNotificationStackScrollLayoutController.getLeft() + + mNotificationStackScrollLayoutController.getWidth() / 2; setHorizontalPanelTranslation(x - center); } @@ -2661,7 +2699,7 @@ public class NotificationPanelViewController extends PanelViewController { } protected void setHorizontalPanelTranslation(float translation) { - mNotificationStackScroller.setTranslationX(translation); + mNotificationStackScrollLayoutController.setTranslationX(translation); mQsFrame.setTranslationX(translation); int size = mVerticalTranslationListener.size(); for (int i = 0; i < size; i++) { @@ -2671,13 +2709,14 @@ public class NotificationPanelViewController extends PanelViewController { protected void updateExpandedHeight(float expandedHeight) { if (mTracking) { - mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); + mNotificationStackScrollLayoutController + .setExpandingVelocity(getCurrentExpandVelocity()); } if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { // The expandedHeight is always the full panel Height when bypassing expandedHeight = getMaxPanelHeightNonBypass(); } - mNotificationStackScroller.setExpandedHeight(expandedHeight); + mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); updateStatusBarIcons(); @@ -2837,7 +2876,7 @@ public class NotificationPanelViewController extends PanelViewController { mHeightListener.onQsHeightChanged(); } }); - mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView()); + mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView()); if (mQs instanceof QSFragment) { mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel()); } @@ -2861,7 +2900,7 @@ public class NotificationPanelViewController extends PanelViewController { if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { mAffordanceHelper.reset(false /* animate */); } - mNotificationStackScroller.setAnimationsEnabled(!disabled); + mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled); } /** @@ -2875,7 +2914,7 @@ public class NotificationPanelViewController extends PanelViewController { if (dozing == mDozing) return; mView.setDozing(dozing); mDozing = dozing; - mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation); + mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation); mKeyguardBottomArea.setDozing(mDozing, animate); if (dozing) { @@ -2903,7 +2942,7 @@ public class NotificationPanelViewController extends PanelViewController { if (!mPulsing && !mDozing) { mAnimateNextPositionUpdate = false; } - mNotificationStackScroller.setPulsing(pulsing, animatePulse); + mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse); mKeyguardStatusView.setPulsing(pulsing); } @@ -3008,7 +3047,7 @@ public class NotificationPanelViewController extends PanelViewController { } public boolean hasActiveClearableNotifications() { - return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL); + return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL); } private void updateShowEmptyShadeView() { @@ -3019,39 +3058,39 @@ public class NotificationPanelViewController extends PanelViewController { } public RemoteInputController.Delegate createRemoteInputDelegate() { - return mNotificationStackScroller.createDelegate(); + return mNotificationStackScrollLayoutController.createDelegate(); } void updateNotificationViews(String reason) { - mNotificationStackScroller.updateSectionBoundaries(reason); - mNotificationStackScroller.updateSpeedBumpIndex(); - mNotificationStackScroller.updateFooter(); + mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); + mNotificationStackScrollLayoutController.updateSpeedBumpIndex(); + mNotificationStackScrollLayoutController.updateFooter(); updateShowEmptyShadeView(); - mNotificationStackScroller.updateIconAreaViews(); + mNotificationStackScrollLayoutController.updateIconAreaViews(); } public void onUpdateRowStates() { - mNotificationStackScroller.onUpdateRowStates(); + mNotificationStackScrollLayoutController.onUpdateRowStates(); } public boolean hasPulsingNotifications() { - return mNotificationStackScroller.hasPulsingNotifications(); + return mNotificationStackScrollLayoutController.hasPulsingNotifications(); } public ActivatableNotificationView getActivatedChild() { - return mNotificationStackScroller.getActivatedChild(); + return mNotificationStackScrollLayoutController.getActivatedChild(); } public void setActivatedChild(ActivatableNotificationView o) { - mNotificationStackScroller.setActivatedChild(o); + mNotificationStackScrollLayoutController.setActivatedChild(o); } public void runAfterAnimationFinished(Runnable r) { - mNotificationStackScroller.runAfterAnimationFinished(r); + mNotificationStackScrollLayoutController.runAfterAnimationFinished(r); } public void setScrollingEnabled(boolean b) { - mNotificationStackScroller.setScrollingEnabled(b); + mNotificationStackScrollLayoutController.setScrollingEnabled(b); } public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, @@ -3060,12 +3099,13 @@ public class NotificationPanelViewController extends PanelViewController { ScrimController scrimController) { setStatusBar(statusBar); setGroupManager(mGroupManager); - mNotificationStackScroller.setNotificationPanelController(this); - mNotificationStackScroller.setIconAreaController(notificationIconAreaController); - mNotificationStackScroller.setStatusBar(statusBar); - mNotificationStackScroller.setGroupManager(groupManager); - mNotificationStackScroller.setShelfController(notificationShelfController); - mNotificationStackScroller.setScrimController(scrimController); + mNotificationStackScrollLayoutController.setNotificationPanelController(this); + mNotificationStackScrollLayoutController + .setIconAreaController(notificationIconAreaController); + mNotificationStackScrollLayoutController.setStatusBar(statusBar); + mNotificationStackScrollLayoutController.setGroupManager(groupManager); + mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); + mNotificationStackScrollLayoutController.setScrimController(scrimController); updateShowEmptyShadeView(); mNotificationShelfController = notificationShelfController; } @@ -3231,7 +3271,8 @@ public class NotificationPanelViewController extends PanelViewController { if (needsAnimation && mInterpolatedDarkAmount == 0) { mAnimateNextPositionUpdate = true; } - ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone(); + ExpandableView firstChildNotGone = + mNotificationStackScrollLayoutController.getFirstChildNotGone(); ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow @@ -3457,13 +3498,13 @@ public class NotificationPanelViewController extends PanelViewController { private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { @Override public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode); + mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode); if (inPinnedMode) { mHeadsUpExistenceChangedRunnable.run(); updateNotificationTranslucency(); } else { setHeadsUpAnimatingAway(true); - mNotificationStackScroller.runAfterAnimationFinished( + mNotificationStackScrollLayoutController.runAfterAnimationFinished( mHeadsUpExistenceChangedRunnable); } updateGestureExclusionRect(); @@ -3475,8 +3516,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onHeadsUpPinned(NotificationEntry entry) { if (!isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), - true); + mNotificationStackScrollLayoutController.generateHeadsUpAnimation( + entry.getHeadsUpAnimationView(), true); } } @@ -3488,7 +3529,7 @@ public class NotificationPanelViewController extends PanelViewController { // notification // will stick to the top without any interaction. if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { - mNotificationStackScroller.generateHeadsUpAnimation( + mNotificationStackScrollLayoutController.generateHeadsUpAnimation( entry.getHeadsUpAnimationView(), false); entry.setHeadsUpIsVisible(); } @@ -3496,7 +3537,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); + mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, isHeadsUp); } } @@ -3511,7 +3552,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); } - mNotificationStackScroller.setMaxTopPadding( + mNotificationStackScrollLayoutController.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); } } @@ -3573,7 +3614,7 @@ public class NotificationPanelViewController extends PanelViewController { } else if (oldState == StatusBarState.SHADE_LOCKED && statusBarState == StatusBarState.KEYGUARD) { animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mNotificationStackScroller.resetScrollPosition(); + mNotificationStackScrollLayoutController.resetScrollPosition(); // Only animate header if the header is visible. If not, it will partially // animate out // the top of QS @@ -3649,7 +3690,7 @@ public class NotificationPanelViewController extends PanelViewController { int oldTop, int oldRight, int oldBottom) { DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); - setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth()); + setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); // Update Clock Pivot mKeyguardStatusView.setPivotX(mView.getWidth() / 2); @@ -3665,7 +3706,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsExpansionHeight = mQsMinExpansionHeight; } mQsMaxExpansionHeight = mQs.getDesiredHeight(); - mNotificationStackScroller.setMaxTopPadding( + mNotificationStackScrollLayoutController.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); } positionClockAndNotifications(); @@ -3727,7 +3768,7 @@ public class NotificationPanelViewController extends PanelViewController { 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p); p.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), - mNotificationStackScroller.getTopPadding(), p); + mNotificationStackScrollLayoutController.getTopPadding(), p); p.setColor(Color.GRAY); canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), mClockPositionResult.clockY, p); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c8fb4e3372c8..6cd33c6872c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -209,6 +209,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.policy.BatteryController; @@ -1075,7 +1076,7 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO (b/136993073) Separate notification shade and status bar mHeadsUpAppearanceController = new HeadsUpAppearanceController( mNotificationIconAreaController, mHeadsUpManager, - mNotificationShadeWindowView, + mStackScroller.getController(), mStatusBarStateController, mKeyguardBypassController, mKeyguardStateController, mWakeUpCoordinator, mCommandQueue, mNotificationPanelViewController, mStatusBarView); @@ -4050,7 +4051,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected IStatusBarService mBarService; // all notifications - protected ViewGroup mStackScroller; + protected NotificationStackScrollLayout mStackScroller; private final NotificationGroupManager mGroupManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 72395e68ff07..ac69d9c32c93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -244,9 +244,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, @Override public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, + boolean appRequestedAuth, NotificationRemoteInputManager.ClickHandler defaultHandler) { final boolean isActivity = pendingIntent.isActivity(); - if (isActivity) { + if (isActivity || appRequestedAuth) { mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent); final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity( pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 4fdc06e64e2c..8f082c15df36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -27,6 +27,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,6 +37,8 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.media.AudioRecordingConfiguration; import android.os.Looper; import android.os.UserHandle; import android.testing.AndroidTestingRunner; @@ -47,9 +52,11 @@ import com.android.systemui.dump.DumpManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; import java.util.List; @SmallTest @@ -73,6 +80,12 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock private PackageManager mPackageManager; + @Mock(stubOnly = true) + private AudioManager mAudioManager; + @Mock(stubOnly = true) + private AudioManager.AudioRecordingCallback mRecordingCallback; + @Mock(stubOnly = true) + private AudioRecordingConfiguration mPausedMockRecording; private AppOpsControllerImpl mController; private TestableLooper mTestableLooper; @@ -94,11 +107,20 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0); + doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0)) + .when(mAudioManager).registerAudioRecordingCallback(any(), any()); + when(mPausedMockRecording.getClientUid()).thenReturn(TEST_UID); + when(mPausedMockRecording.isClientSilenced()).thenReturn(true); + + when(mAudioManager.getActiveRecordingConfigurations()) + .thenReturn(List.of(mPausedMockRecording)); + mController = new AppOpsControllerImpl( mContext, mTestableLooper.getLooper(), mDumpManager, - mFlagsCache + mFlagsCache, + mAudioManager ); } @@ -363,6 +385,89 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); } + @Test + public void testPausedRecordingIsRetrievedOnCreation() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + verify(mCallback, never()) + .onActiveStateChanged(anyInt(), anyInt(), anyString(), anyBoolean()); + } + + @Test + public void testPausedRecordingFilteredOut() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + assertTrue(mController.getActiveAppOps().isEmpty()); + } + + @Test + public void testOnlyRecordAudioPaused() { + mController.addCallback(new int[]{ + AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_CAMERA + }, mCallback); + mTestableLooper.processAllMessages(); + + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); + List<AppOpItem> list = mController.getActiveAppOps(); + + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + } + + @Test + public void testUnpausedRecordingSentActive() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + + mTestableLooper.processAllMessages(); + mRecordingCallback.onRecordingConfigChanged(Collections.emptyList()); + + mTestableLooper.processAllMessages(); + + verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + } + + @Test + public void testAudioPausedSentInactive() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + + AudioRecordingConfiguration mockARC = mock(AudioRecordingConfiguration.class); + when(mockARC.getClientUid()).thenReturn(TEST_UID_OTHER); + when(mockARC.isClientSilenced()).thenReturn(true); + + mRecordingCallback.onRecordingConfigChanged(List.of(mockARC)); + mTestableLooper.processAllMessages(); + + InOrder inOrder = inOrder(mCallback); + inOrder.verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + inOrder.verify(mCallback).onActiveStateChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 3789e6ef1f65..a4ebe1ff2a4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -13,6 +13,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.eq @@ -58,6 +59,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener @Mock lateinit var mediaResumeListener: MediaResumeListener @Mock lateinit var pendingIntent: PendingIntent + @Mock lateinit var activityStarter: ActivityStarter @JvmField @Rule val mockito = MockitoJUnit.rule() lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification @@ -68,8 +70,8 @@ class MediaDataManagerTest : SysuiTestCase() { backgroundExecutor = FakeExecutor(FakeSystemClock()) mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory, broadcastDispatcher, dumpManager, - mediaTimeoutListener, mediaResumeListener, useMediaResumption = true, - useQsMediaPlayer = true) + mediaTimeoutListener, mediaResumeListener, activityStarter, + useMediaResumption = true, useQsMediaPlayer = true) session = MediaSession(context, "MediaDataManagerTestSession") mediaNotification = SbnBuilder().run { setPkg(PACKAGE_NAME) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 85701c24f7d6..fec467706b7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -212,7 +212,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, - true /* allowLongPress */, mNotificationRoundnessManager, + mNotificationRoundnessManager, mock(DynamicPrivacyController.class), mock(SysuiStatusBarStateController.class), mHeadsUpManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index e546dff8abf6..8dea84c4d0b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -51,8 +51,8 @@ import org.junit.runner.RunWith; @RunWithLooper public class HeadsUpAppearanceControllerTest extends SysuiTestCase { - private final NotificationStackScrollLayout mStackScroller = - mock(NotificationStackScrollLayout.class); + private final NotificationStackScrollLayoutController mStackScrollerController = + mock(NotificationStackScrollLayoutController.class); private final NotificationPanelViewController mPanelView = mock(NotificationPanelViewController.class); private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class); @@ -93,9 +93,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mWakeUpCoordinator, mKeyguardStateController, mCommandQueue, - mHeadsUpStatusBarView, - mStackScroller, + mStackScrollerController, mPanelView, + mHeadsUpStatusBarView, new View(mContext), mOperatorNameView, new View(mContext)); @@ -172,9 +172,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mWakeUpCoordinator, mKeyguardStateController, mCommandQueue, - mHeadsUpStatusBarView, - mStackScroller, + mStackScrollerController, mPanelView, + mHeadsUpStatusBarView, new View(mContext), new View(mContext), new View(mContext)); @@ -193,14 +193,14 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { reset(mHeadsUpManager); reset(mDarkIconDispatcher); reset(mPanelView); - reset(mStackScroller); + reset(mStackScrollerController); mHeadsUpAppearanceController.destroy(); verify(mHeadsUpManager).removeListener(any()); verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any()); verify(mPanelView).removeVerticalTranslationListener(any()); verify(mPanelView).removeTrackingHeadsUpListener(any()); verify(mPanelView).setHeadsUpAppearanceController(any()); - verify(mStackScroller).removeOnExpandedHeightChangedListener(any()); - verify(mStackScroller).removeOnLayoutChangeListener(any()); + verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any()); + verify(mStackScrollerController).removeOnLayoutChangeListener(any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index bf4ccd22effd..f66fd5677a5d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -72,8 +72,10 @@ import com.android.systemui.statusbar.notification.ConversationNotificationManag import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -186,8 +188,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; - private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; - private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -212,7 +212,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class)); when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); - mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics); + FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( + mDisplayMetrics); doAnswer((Answer<Void>) invocation -> { mTouchHandler = invocation.getArgument(0); @@ -232,6 +233,11 @@ public class NotificationPanelViewTest extends SysuiTestCase { mock(NotificationRoundnessManager.class), mStatusBarStateController, new FalsingManagerFake()); + NotificationStackScrollLayoutController notificationStackScrollLayoutController = + new NotificationStackScrollLayoutController( + true /* allowLongPress */, + mock(NotificationGutsManager.class) + ); mNotificationPanelViewController = new NotificationPanelViewController(mView, mInjectionInflationController, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, @@ -241,10 +247,11 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController, - mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, - () -> mKeyguardClockSwitchController); + () -> mKeyguardClockSwitchController, + notificationStackScrollLayoutController); mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager, mNotificationShelfController, mNotificationAreaController, mScrimController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); @@ -252,8 +259,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = ArgumentCaptor.forClass(View.AccessibilityDelegate.class); - verify(mView) - .setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); + verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue(); } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index c45da8668087..a2d58c8019fc 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -631,7 +631,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // Two pointers moving in the same direction within // a given distance perform a drag. mState.startDragging(); - adjustEventLocationForDrag(event); + computeDraggingPointerIdIfNeeded(event); pointerIdBits = 1 << mDraggingPointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); mDispatcher.sendMotionEvent( @@ -794,7 +794,7 @@ public class TouchExplorer extends BaseEventStreamTransformation case 2: if (isDraggingGesture(event)) { // If still dragging send a drag event. - adjustEventLocationForDrag(event); + computeDraggingPointerIdIfNeeded(event); mDispatcher.sendMotionEvent( event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags); } else { @@ -959,44 +959,27 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Adjust the location of an injected event when performing a drag. The location will be the - * location of the finger closest to an edge of the screen. + * Computes {@link #mDraggingPointerId} if it is invalid. The pointer will be the finger + * closet to an edge of the screen. */ - private void adjustEventLocationForDrag(MotionEvent event) { + private void computeDraggingPointerIdIfNeeded(MotionEvent event) { + if (mDraggingPointerId != INVALID_POINTER_ID) { + // If we have a valid pointer ID, we should be good + final int pointerIndex = event.findPointerIndex(mDraggingPointerId); + if (event.findPointerIndex(pointerIndex) >= 0) { + return; + } + } + // Use the pointer that is closest to its closest edge. final float firstPtrX = event.getX(0); final float firstPtrY = event.getY(0); final int firstPtrId = event.getPointerId(0); final float secondPtrX = event.getX(1); final float secondPtrY = event.getY(1); final int secondPtrId = event.getPointerId(1); - float draggingX = firstPtrX; - float draggingY = firstPtrY; - if (mDraggingPointerId != INVALID_POINTER_ID) { - // Just use the coordinates of the dragging pointer. - int pointerIndex = event.findPointerIndex(mDraggingPointerId); - if (pointerIndex >= 0) { - draggingX = event.getX(pointerIndex); - draggingY = event.getY(pointerIndex); - } else { - // We've lost track of the dragging pointer. Try to recover by invalidating it. - // We'll the drop into the code below to choose a new one. - mDraggingPointerId = INVALID_POINTER_ID; - } - } - // Not quite an else, since the above code can invalidate the pointer - if (mDraggingPointerId == INVALID_POINTER_ID) { - // The goal is to use the coordinates of the finger that is closest to its closest edge. - if (getDistanceToClosestEdge(firstPtrX, firstPtrY) - < getDistanceToClosestEdge(secondPtrX, secondPtrY)) { - // X and Y initialized to firstPtrX and Y was right - mDraggingPointerId = firstPtrId; - } else { - draggingX = secondPtrX; - draggingY = secondPtrY; - mDraggingPointerId = secondPtrId; - } - } - event.setLocation(draggingX, draggingY); + mDraggingPointerId = (getDistanceToClosestEdge(firstPtrX, firstPtrY) + < getDistanceToClosestEdge(secondPtrX, secondPtrY)) + ? firstPtrId : secondPtrId; } private float getDistanceToClosestEdge(float x, float y) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3eb26de3a675..b83aa4fb4d86 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -694,12 +694,8 @@ public final class ActiveServices { } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, - callingUid, service, r, allowBackgroundActivityStarts); - } - + setFgsRestrictionLocked(callingPackage, callingPid, + callingUid, service, r, allowBackgroundActivityStarts); return cmp; } @@ -1369,6 +1365,7 @@ public final class ActiveServices { + r.shortInstanceName); } } + boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; if (r.fgRequired) { @@ -1415,6 +1412,24 @@ public final class ActiveServices { ignoreForeground = true; } + if (!ignoreForeground) { + if (!r.mAllowStartForeground) { + if (!r.mLoggedInfoAllowStartForeground) { + Slog.wtf(TAG, "Background started FGS " + + r.mInfoAllowStartForeground); + r.mLoggedInfoAllowStartForeground = true; + } + if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + Slog.w(TAG, + "Service.startForeground() not allowed due to " + + " mAllowStartForeground false: service " + + r.shortInstanceName); + updateServiceForegroundLocked(r.app, true); + ignoreForeground = true; + } + } + } + // Apps under strict background restrictions simply don't get to have foreground // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app @@ -2067,12 +2082,7 @@ public final class ActiveServices { } } - if (!s.mAllowWhileInUsePermissionInFgs) { - s.mAllowWhileInUsePermissionInFgs = - shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - callingPid, callingUid, - service, s, false); - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false); if (s.app != null) { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { @@ -4840,21 +4850,48 @@ public final class ActiveServices { } /** - * Should allow while-in-use permissions in foreground service or not. - * while-in-use permissions in FGS started from background might be restricted. + * There are two FGS restrictions: + * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground + * service or not. while-in-use permissions in FGS started from background might be restricted. + * In S, mAllowStartForeground is to allow FGS to startForeground or not. Service started + * from background may not become a FGS. * @param callingPackage caller app's package name. * @param callingUid caller app's uid. * @param intent intent to start/bind service. * @param r the service to start. * @return true if allow, false otherwise. */ - private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage, + private void setFgsRestrictionLocked(String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { - // Is the background FGS start restriction turned on? + // Check DeviceConfig flag. if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { - return true; + r.mAllowWhileInUsePermissionInFgs = true; + } + + if (!r.mAllowWhileInUsePermissionInFgs || !r.mAllowStartForeground) { + final boolean temp = shouldAllowFgsFeatureLocked(callingPackage, callingPid, + callingUid, intent, r, allowBackgroundActivityStarts); + if (!r.mAllowWhileInUsePermissionInFgs) { + r.mAllowWhileInUsePermissionInFgs = temp; + } + if (!r.mAllowStartForeground) { + r.mAllowStartForeground = temp; + } } + } + + /** + * Should allow FGS feature or not. + * @param callingPackage caller app's package name. + * @param callingUid caller app's uid. + * @param intent intent to start/bind service. + * @param r the service to start. + * @return true if allow, false otherwise. + */ + private boolean shouldAllowFgsFeatureLocked(String callingPackage, + int callingPid, int callingUid, Intent intent, ServiceRecord r, + boolean allowBackgroundActivityStarts) { // Is the allow activity background start flag on? if (allowBackgroundActivityStarts) { return true; @@ -4894,10 +4931,11 @@ public final class ActiveServices { } // Is the calling UID at PROCESS_STATE_TOP or above? - final boolean isCallingUidTopApp = appIsTopLocked(callingUid); - if (isCallingUidTopApp) { + final int uidState = mAm.getUidState(callingUid); + if (uidState <= ActivityManager.PROCESS_STATE_TOP) { return true; } + // Does the calling UID have any visible activity? final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid); if (isCallingUidVisible) { @@ -4915,6 +4953,19 @@ public final class ActiveServices { if (isDeviceOwner) { return true; } + + final String info = + "[callingPackage: " + callingPackage + + "; callingUid: " + callingUid + + "; uidState: " + ProcessList.makeProcStateString(uidState) + + "; intent: " + intent + + "; targetSdkVersion:" + r.appInfo.targetSdkVersion + + "]"; + if (!info.equals(r.mInfoAllowStartForeground)) { + r.mLoggedInfoAllowStartForeground = false; + r.mInfoAllowStartForeground = info; + } + return false; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 775119c18037..e9539be8b0d5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -144,6 +144,13 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED = "default_background_fgs_starts_restriction_enabled"; + /** + * Default value for mFlagFgsStartRestrictionEnabled if not explicitly set in + * Settings.Global. + */ + private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED = + "default_fgs_starts_restriction_enabled"; + // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -293,6 +300,11 @@ final class ActivityManagerConstants extends ContentObserver { // started, the restriction is on while-in-use permissions.) volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true; + // Indicates whether the foreground service background start restriction is enabled. + // When the restriction is enabled, service is not allowed to startForeground from background + // at all. + volatile boolean mFlagFgsStartRestrictionEnabled = false; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -436,6 +448,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED: updateBackgroundFgsStartsRestriction(); break; + case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED: + updateFgsStartsRestriction(); + break; case KEY_OOMADJ_UPDATE_POLICY: updateOomAdjUpdatePolicy(); break; @@ -659,6 +674,7 @@ final class ActivityManagerConstants extends ContentObserver { mFlagForegroundServiceStartsLoggingEnabled = Settings.Global.getInt(mResolver, Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, 1) == 1; } + private void updateBackgroundFgsStartsRestriction() { mFlagBackgroundFgsStartRestrictionEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -666,6 +682,13 @@ final class ActivityManagerConstants extends ContentObserver { /*defaultValue*/ true); } + private void updateFgsStartsRestriction() { + mFlagFgsStartRestrictionEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED, + /*defaultValue*/ false); + } + private void updateOomAdjUpdatePolicy() { OOMADJ_UPDATE_QUICK = DeviceConfig.getInt( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a838aadc404e..f250647d7711 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1581,6 +1581,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; static final int KILL_APP_ZYGOTE_MSG = 71; static final int BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG = 72; + static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1917,6 +1918,11 @@ public class ActivityManagerService extends IActivityManager.Stub case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: { handleBinderHeavyHitterAutoSamplerTimeOut(); } break; + case WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG: { + synchronized (ActivityManagerService.this) { + ((ContentProviderRecord) msg.obj).onProviderPublishStatusLocked(false); + } + } break; } } } diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 5cc7aba736e1..bfba4afcd4e4 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -50,6 +50,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; +import android.os.Message; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -218,7 +219,7 @@ public class ContentProviderHelper { // of being published... but it is also allowed to run // in the caller's process, so don't make a connection // and just let the caller instantiate its own instance. - ContentProviderHolder holder = cpr.newHolder(null); + ContentProviderHolder holder = cpr.newHolder(null, true); // don't give caller the provider object, it needs to make its own. holder.provider = null; return holder; @@ -415,7 +416,7 @@ public class ContentProviderHelper { // info and allow the caller to instantiate it. Only do // this if the provider is the same user as the caller's // process, or can run as root (so can be in any process). - return cpr.newHolder(null); + return cpr.newHolder(null, true); } if (ActivityManagerDebugConfig.DEBUG_PROVIDER) { @@ -513,6 +514,38 @@ public class ContentProviderHelper { UserHandle.getAppId(cpi.applicationInfo.uid)); } + if (caller != null) { + // The client will be waiting, and we'll notify it when the provider is ready. + synchronized (cpr) { + if (cpr.provider == null) { + if (cpr.launchingApp == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + UserHandle.getUserId(cpi.applicationInfo.uid), + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; + } + + if (conn != null) { + conn.waiting = true; + } + Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG); + msg.obj = cpr; + mService.mHandler.sendMessageDelayed(msg, + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + } + } + // Return a holder instance even if we are waiting for the publishing of the provider, + // client will check for the holder.provider to see if it needs to wait for it. + return cpr.newHolder(conn, false); + } + + // Because of the provider's external client (i.e., SHELL), we'll have to wait right here. // Wait for the provider to be published... final long timeout = SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS; @@ -569,7 +602,7 @@ public class ContentProviderHelper { + " caller=" + callerName + "/" + Binder.getCallingUid()); return null; } - return cpr.newHolder(conn); + return cpr.newHolder(conn, false); } void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { @@ -622,6 +655,8 @@ public class ContentProviderHelper { } if (wasInLaunchingProviders) { mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst); + mService.mHandler.removeMessages( ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } // Make sure the package is associated with the process. @@ -635,6 +670,7 @@ public class ContentProviderHelper { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); + dst.onProviderPublishStatusLocked(true); } dst.mRestartCount = 0; mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); @@ -1504,6 +1540,9 @@ public class ContentProviderHelper { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); + cpr.onProviderPublishStatusLocked(false); + mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, cpr); } final int userId = UserHandle.getUserId(cpr.uid); // Don't remove from provider map if it doesn't match diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index d8d8cccc05f3..fb8b5d480b27 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -85,11 +85,12 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { noReleaseNeeded = cpr.noReleaseNeeded; } - public ContentProviderHolder newHolder(ContentProviderConnection conn) { + public ContentProviderHolder newHolder(ContentProviderConnection conn, boolean local) { ContentProviderHolder holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = noReleaseNeeded; holder.connection = conn; + holder.mLocal = local; return holder; } @@ -179,6 +180,50 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { return !connections.isEmpty() || hasExternalProcessHandles(); } + /** + * Notify all clients that the provider has been published and ready to use, + * or timed out. + * + * @param status true: successfully published; false: timed out + */ + void onProviderPublishStatusLocked(boolean status) { + final int numOfConns = connections.size(); + final int userId = UserHandle.getUserId(appInfo.uid); + for (int i = 0; i < numOfConns; i++) { + final ContentProviderConnection conn = connections.get(i); + if (conn.waiting && conn.client != null) { + final ProcessRecord client = conn.client; + if (!status) { + if (launchingApp == null) { + Slog.w(TAG_AM, "Unable to launch app " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + userId, + appInfo.packageName, + appInfo.uid, info.authority); + } else { + Slog.wtf(TAG_AM, "Timeout waiting for provider " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + + " caller=" + client); + } + } + if (client.thread != null) { + try { + client.thread.notifyContentProviderPublishStatus( + newHolder(status ? conn : null, false), + info.authority, userId, status); + } catch (RemoteException e) { + } + } + } + conn.waiting = false; + } + } + void dump(PrintWriter pw, String prefix, boolean full) { if (full) { pw.print(prefix); pw.print("package="); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 4a2703056871..66677b67b6aa 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -146,6 +146,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // the most recent package that start/bind this service. String mRecentCallingPackage; + // allow the service becomes foreground service? Service started from background may not be + // allowed to become a foreground service. + boolean mAllowStartForeground; + String mInfoAllowStartForeground; + boolean mLoggedInfoAllowStartForeground; + String stringName; // caching of toString private int lastStartId; // identifier of most recent start request. @@ -408,6 +414,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); + pw.print(prefix); pw.print("allowStartForeground="); + pw.println(mAllowStartForeground); + pw.print(prefix); pw.print("infoAllowStartForeground="); + pw.println(mInfoAllowStartForeground); if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index b94396634530..23b09294260c 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1282,6 +1282,7 @@ public class AudioService extends IAudioService.Stub } if (isPlatformTelevision()) { + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller); synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiPlaybackClient != null) { updateHdmiCecSinkLocked(mHdmiCecSink | false); @@ -1301,54 +1302,22 @@ public class AudioService extends IAudioService.Stub } } - /** - * Update volume states for the given device. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param caller caller of this method - */ - private void updateVolumeStatesForAudioDevice(int device, String caller) { + private void checkAddAllFixedVolumeDevices(int device, String caller) { final int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = 0; streamType < numStreamTypes; streamType++) { - updateVolumeStates(device, streamType, caller); - } - } - - /** - * Update volume states for the given device and given stream. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param streamType streamType to be updated - * @param caller caller of this method - */ - private void updateVolumeStates(int device, int streamType, String caller) { - if (!mStreamStates[streamType].hasIndexForDevice(device)) { - // set the default value, if device is affected by a full/fix/abs volume rule, it - // will taken into account in checkFixedVolumeDevices() - mStreamStates[streamType].setIndex( - mStreamStates[mStreamVolumeAlias[streamType]] - .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), - device, caller, true /*hasModifyAudioSettings*/); - } - - // Check if device to be updated is routed for the given audio stream - List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributes( - new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); - for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) { - if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType( - device)) { - mStreamStates[streamType].checkFixedVolumeDevices(); + if (!mStreamStates[streamType].hasIndexForDevice(device)) { + // set the default value, if device is affected by a full/fix/abs volume rule, it + // will taken into account in checkFixedVolumeDevices() + mStreamStates[streamType].setIndex( + mStreamStates[mStreamVolumeAlias[streamType]] + .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), + device, caller, true /*hasModifyAudioSettings*/); + } + mStreamStates[streamType].checkFixedVolumeDevices(); - // Unmute streams if required if device is full volume - if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) { - mStreamStates[streamType].mute(false); - } + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); } } } @@ -4932,15 +4901,7 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { for (int stream = 0; stream < mStreamStates.length; stream++) { if (stream != skipStream) { - int devices = mStreamStates[stream].observeDevicesForStream_syncVSS( - false /*checkOthers*/); - - Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices); - for (Integer device : devicesSet) { - // Update volume states for devices routed for the stream - updateVolumeStates(device, stream, - "AudioService#observeDevicesForStreams"); - } + mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/); } } } @@ -5009,7 +4970,7 @@ public class AudioService extends IAudioService.Stub + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller)); // make sure we have a volume entry for this device, and that volume is updated according // to volume behavior - updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); + checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); } /** @@ -7231,9 +7192,10 @@ public class AudioService extends IAudioService.Stub // HDMI output removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI); } - updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI, - "HdmiPlaybackClient.DisplayStatusCallback"); } + + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, + "HdmiPlaybackClient.DisplayStatusCallback"); } private class MyHdmiControlStatusChangeListenerCallback diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 840645edcb82..8af74631fb2e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -613,6 +613,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new IllegalArgumentException( "APEX files can only be installed as part of a staged session."); } + if (params.isMultiPackage) { + throw new IllegalArgumentException("A multi-session can't be set as APEX."); + } } if (params.isStaged && !isCalledBySystemOrShell(callingUid)) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 9dda3812fccf..f66b4ee5a1a7 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -141,6 +141,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -443,6 +444,11 @@ public class UserManagerService extends IUserManager.Stub { } }; + // TODO(b/161915546): remove once userWithName() is fixed / removed + // Use to debug / dump when user 0 is allocated at userWithName() + public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE + public final AtomicInteger mUser0Allocations; + /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -629,6 +635,7 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); + mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -1310,6 +1317,10 @@ public class UserManagerService extends IUserManager.Stub { */ private UserInfo userWithName(UserInfo orig) { if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { + if (DBG_ALLOCATION) { + final int number = mUser0Allocations.incrementAndGet(); + Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); + } UserInfo withName = new UserInfo(orig); withName.name = getOwnerName(); return withName; @@ -4833,6 +4844,10 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is split-system user: " + UserManager.isSplitSystemUser()); pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); + pw.println(" Owner name: " + getOwnerName()); + if (DBG_ALLOCATION) { + pw.println(" System user allocations: " + mUser0Allocations.get()); + } // Dump UserTypes pw.println(); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 03771be0ce7c..63aa80e8269a 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -83,6 +83,7 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; +import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -120,6 +121,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -2521,12 +2523,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionsState permissionsState = ps.getPermissionsState(); - final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); + final int[] userIds = getAllUserIds(); boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; - for (int userId : currentUserIds) { + for (int userId : userIds) { if (permissionsState.isMissing(userId)) { Collection<String> requestedPermissions; int targetSdkVersion; @@ -2592,7 +2594,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // runtime and revocation of a runtime from a shared user. synchronized (mLock) { updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(), - currentUserIds); + userIds); if (!ArrayUtils.isEmpty(updatedUserIds)) { runtimePermissionsRevoked = true; } @@ -2747,7 +2749,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // a runtime permission being downgraded to an install one. // Also in permission review mode we keep dangerous permissions // for legacy apps - for (int userId : currentUserIds) { + for (int userId : userIds) { if (origPermissions.getRuntimePermissionState( perm, userId) != null) { // Revoke the runtime permission and clear the flags. @@ -2770,7 +2772,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean hardRestricted = bp.isHardRestricted(); boolean softRestricted = bp.isSoftRestricted(); - for (int userId : currentUserIds) { + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -2909,7 +2911,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean hardRestricted = bp.isHardRestricted(); boolean softRestricted = bp.isSoftRestricted(); - for (int userId : currentUserIds) { + for (int userId : userIds) { // If permission policy is not ready we don't deal with restricted // permissions as the policy may whitelist some permissions. Once // the policy is initialized we would re-evaluate permissions. @@ -3061,10 +3063,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg, - currentUserIds, updatedUserIds); + userIds, updatedUserIds); updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, - permissionsState, pkg, newImplicitPermissions, currentUserIds, updatedUserIds); - updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, currentUserIds, + permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds); + updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, updatedUserIds); } @@ -3081,6 +3083,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * Returns all relevant user ids. This list include the current set of created user ids as well + * as pre-created user ids. + * @return user ids for created users and pre-created users + */ + private int[] getAllUserIds() { + final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); + t.traceBegin("getAllUserIds"); + List<UserInfo> users = UserManagerService.getInstance().getUsers( + /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false); + int size = users.size(); + final int[] userIds = new int[size]; + for (int i = 0; i < size; i++) { + userIds[i] = users.get(i).id; + } + t.traceEnd(); + return userIds; + } + + /** * Revoke permissions that are not implicit anymore and that have * {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set. * diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 2a74b3d23829..5aedfc19028b 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -28,8 +28,6 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.annotation.NonNull; import android.app.AppOpsManager; -import android.app.role.OnRoleHoldersChangedListener; -import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; @@ -41,9 +39,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Binder; import android.os.Handler; @@ -65,7 +61,6 @@ import com.android.internal.app.AssistUtils; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -77,10 +72,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.util.ArrayList; -import java.util.List; import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { @@ -88,16 +80,13 @@ public class SliceManagerService extends ISliceManager.Stub { private final Object mLock = new Object(); private final Context mContext; - private final PackageManagerInternal mPackageManagerInternal; private final AppOpsManager mAppOps; private final AssistUtils mAssistUtils; @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); @GuardedBy("mLock") - private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>(); - @GuardedBy("mLock") - private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>(); + private final SparseArray<String> mLastAssistantPackage = new SparseArray<>(); private final Handler mHandler; private final SlicePermissionManager mPermissions; @@ -110,8 +99,6 @@ public class SliceManagerService extends ISliceManager.Stub { @VisibleForTesting SliceManagerService(Context context, Looper looper) { mContext = context; - mPackageManagerInternal = Objects.requireNonNull( - LocalServices.getService(PackageManagerInternal.class)); mAppOps = context.getSystemService(AppOpsManager.class); mAssistUtils = new AssistUtils(context); mHandler = new Handler(looper); @@ -124,7 +111,6 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); - mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } @@ -174,8 +160,7 @@ public class SliceManagerService extends ISliceManager.Stub { mHandler.post(() -> { if (slicePkg != null && !Objects.equals(pkg, slicePkg)) { mAppUsageStats.reportEvent(slicePkg, user, - isAssistant(pkg, user) || isDefaultHomeApp(pkg, user) - ? SLICE_PINNED_PRIV : SLICE_PINNED); + isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED); } }); } @@ -440,38 +425,19 @@ public class SliceManagerService extends ISliceManager.Stub { private boolean hasFullSliceAccess(String pkg, int userId) { long ident = Binder.clearCallingIdentity(); try { - boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) - || isGrantedFullAccess(pkg, userId); - return ret; + return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId); } finally { Binder.restoreCallingIdentity(ident); } } private boolean isAssistant(String pkg, int userId) { - return getAssistantMatcher(userId).matches(pkg); - } - - private boolean isDefaultHomeApp(String pkg, int userId) { - return getHomeMatcher(userId).matches(pkg); - } - - private PackageMatchingCache getAssistantMatcher(int userId) { - PackageMatchingCache matcher = mAssistantLookup.get(userId); - if (matcher == null) { - matcher = new PackageMatchingCache(() -> getAssistant(userId)); - mAssistantLookup.put(userId, matcher); - } - return matcher; - } - - private PackageMatchingCache getHomeMatcher(int userId) { - PackageMatchingCache matcher = mHomeLookup.get(userId); - if (matcher == null) { - matcher = new PackageMatchingCache(() -> getDefaultHome(userId)); - mHomeLookup.put(userId, matcher); + if (pkg == null) return false; + if (!pkg.equals(mLastAssistantPackage.get(userId))) { + // Failed on cached value, try updating. + mLastAssistantPackage.put(userId, getAssistant(userId)); } - return matcher; + return pkg.equals(mLastAssistantPackage.get(userId)); } private String getAssistant(int userId) { @@ -482,111 +448,6 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); } - /** - * A cached value of the default home app - */ - private String mCachedDefaultHome = null; - - // Based on getDefaultHome in ShortcutService. - // TODO: Unify if possible - @VisibleForTesting - protected String getDefaultHome(int userId) { - - // Set VERIFY to true to run the cache in "shadow" mode for cache - // testing. Do not commit set to true; - final boolean VERIFY = false; - - if (mCachedDefaultHome != null) { - if (!VERIFY) { - return mCachedDefaultHome; - } - } - - final long token = Binder.clearCallingIdentity(); - try { - final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); - - // Default launcher from package manager. - final ComponentName defaultLauncher = mPackageManagerInternal - .getHomeActivitiesAsUser(allHomeCandidates, userId); - - ComponentName detected = defaultLauncher; - - // Cache the default launcher. It is not a problem if the - // launcher is null - eventually, the default launcher will be - // set to something non-null. - mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null); - - if (detected == null) { - // If we reach here, that means it's the first check since the user was created, - // and there's already multiple launchers and there's no default set. - // Find the system one with the highest priority. - // (We need to check the priority too because of FallbackHome in Settings.) - // If there's no system launcher yet, then no one can access slices, until - // the user explicitly sets one. - final int size = allHomeCandidates.size(); - - int lastPriority = Integer.MIN_VALUE; - for (int i = 0; i < size; i++) { - final ResolveInfo ri = allHomeCandidates.get(i); - if (!ri.activityInfo.applicationInfo.isSystemApp()) { - continue; - } - if (ri.priority < lastPriority) { - continue; - } - detected = ri.activityInfo.getComponentName(); - lastPriority = ri.priority; - } - } - final String ret = ((detected != null) ? detected.getPackageName() : null); - if (VERIFY) { - if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) { - Slog.e(TAG, "getDefaultHome() cache failure, is " + - mCachedDefaultHome + " should be " + ret); - } - } - return ret; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public void invalidateCachedDefaultHome() { - mCachedDefaultHome = null; - } - - /** - * Listen for changes in the roles, and invalidate the cached default - * home as necessary. - */ - private RoleObserver mRoleObserver; - - class RoleObserver implements OnRoleHoldersChangedListener { - private RoleManager mRm; - private final Executor mExecutor; - - RoleObserver() { - mExecutor = mContext.getMainExecutor(); - register(); - } - - public void register() { - mRm = mContext.getSystemService(RoleManager.class); - if (mRm != null) { - mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL); - invalidateCachedDefaultHome(); - } - } - - @Override - public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { - if (RoleManager.ROLE_HOME.equals(roleName)) { - invalidateCachedDefaultHome(); - } - } - } - private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); } @@ -635,30 +496,6 @@ public class SliceManagerService extends ISliceManager.Stub { return mPermissions.getAllPackagesGranted(pkg); } - /** - * Holder that caches a package that has access to a slice. - */ - static class PackageMatchingCache { - - private final Supplier<String> mPkgSource; - private String mCurrentPkg; - - public PackageMatchingCache(Supplier<String> pkgSource) { - mPkgSource = pkgSource; - } - - public boolean matches(String pkgCandidate) { - if (pkgCandidate == null) return false; - - if (Objects.equals(pkgCandidate, mCurrentPkg)) { - return true; - } - // Failed on cached value, try updating. - mCurrentPkg = mPkgSource.get(); - return Objects.equals(pkgCandidate, mCurrentPkg); - } - } - public static class Lifecycle extends SystemService { private SliceManagerService mService; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5b8078729eee..e4f28546a5fd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2549,7 +2549,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task stack = getRootTask(); final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null) - && stack.isFocusedStackOnDisplay(); + && stack.isFocusedStackOnDisplay() + // Do not adjust focus task because the task will be reused to launch new activity. + && !task.isClearingToReuseTask(); final boolean shouldAdjustGlobalFocus = mayAdjustTop // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index f7cb0146ea52..15e88fc44746 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2023,8 +2023,6 @@ class ActivityStarter { // of history or if it is finished immediately), thus disassociating the task. Also note // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked} // launching another activity. - // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are - // already launching one. targetTask.performClearTaskLocked(); targetTask.setIntent(mStartActivity); mAddingToTask = true; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ba5a38290349..affbafa095cc 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1557,7 +1557,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // the heavy operations. This also benefits that the states of multiple activities // are handled together. r.linkFixedRotationTransform(prevRotatedLaunchingApp); - setFixedRotationLaunchingAppUnchecked(r, rotation); + if (r != mFixedRotationTransitionListener.mAnimatingRecents) { + // Only update the record for normal activity so the display orientation can be + // updated when the transition is done if it becomes the top. And the case of + // recents can be handled when the recents animation is finished. + setFixedRotationLaunchingAppUnchecked(r, rotation); + } return; } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index df5356303f8b..3c64ffb237d6 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1627,8 +1627,8 @@ class RecentTasks { } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at " + topIndex + " from intial " + taskIndex); - // Find the end of the chain, doing a sanity check along the way. - boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId; + // Find the end of the chain, doing a validity check along the way. + boolean isValid = top.mAffiliatedTaskId == task.mAffiliatedTaskId; int endIndex = topIndex; Task prev = top; while (endIndex < recentsCount) { @@ -1640,7 +1640,7 @@ class RecentTasks { if (cur.mNextAffiliate != null || cur.mNextAffiliateTaskId != INVALID_TASK_ID) { Slog.wtf(TAG, "Bad chain @" + endIndex + ": first task has next affiliate: " + prev); - sane = false; + isValid = false; break; } } else { @@ -1652,7 +1652,7 @@ class RecentTasks { + " has bad next affiliate " + cur.mNextAffiliate + " id " + cur.mNextAffiliateTaskId + ", expected " + prev); - sane = false; + isValid = false; break; } } @@ -1662,7 +1662,7 @@ class RecentTasks { Slog.wtf(TAG, "Bad chain @" + endIndex + ": last task " + cur + " has previous affiliate " + cur.mPrevAffiliate); - sane = false; + isValid = false; } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex); break; @@ -1673,7 +1673,7 @@ class RecentTasks { + ": task " + cur + " has previous affiliate " + cur.mPrevAffiliate + " but should be id " + cur.mPrevAffiliate); - sane = false; + isValid = false; break; } } @@ -1682,7 +1682,7 @@ class RecentTasks { + ": task " + cur + " has affiliated id " + cur.mAffiliatedTaskId + " but should be " + task.mAffiliatedTaskId); - sane = false; + isValid = false; break; } prev = cur; @@ -1690,18 +1690,18 @@ class RecentTasks { if (endIndex >= recentsCount) { Slog.wtf(TAG, "Bad chain ran off index " + endIndex + ": last task " + prev); - sane = false; + isValid = false; break; } } - if (sane) { + if (isValid) { if (endIndex < taskIndex) { Slog.wtf(TAG, "Bad chain @" + endIndex + ": did not extend to task " + task + " @" + taskIndex); - sane = false; + isValid = false; } } - if (sane) { + if (isValid) { // All looks good, we can just move all of the affiliated tasks // to the top. for (int i=topIndex; i<=endIndex; i++) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b3a3ed7eeba4..fe2d08f6a4c2 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -737,7 +737,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> winAnimator.mSession.mPid, operation); final long callingIdentity = Binder.clearCallingIdentity(); try { - // There was some problem...first, do a sanity check of the window list to make sure + // There was some problem...first, do a validity check of the window list to make sure // we haven't left any dangling surfaces around. Slog.i(TAG_WM, "Out of memory for surface! Looking for leaks..."); @@ -811,7 +811,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // "Something has changed! Let's make it correct now." - // TODO: Super crazy long method that should be broken down... + // TODO: Super long method that should be broken down... void performSurfacePlacementNoTrace() { if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by " + Debug.getCallers(3)); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ec7b1eed7f91..0529abf89f6e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1836,14 +1836,25 @@ class Task extends WindowContainer<WindowContainer> { */ void performClearTaskLocked() { mReuseTask = true; - performClearTask("clear-task-all"); - mReuseTask = false; + mStackSupervisor.beginDeferResume(); + try { + performClearTask("clear-task-all"); + } finally { + mStackSupervisor.endDeferResume(); + mReuseTask = false; + } } ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) { mReuseTask = true; - final ActivityRecord result = performClearTaskLocked(newR, launchFlags); - mReuseTask = false; + mStackSupervisor.beginDeferResume(); + final ActivityRecord result; + try { + result = performClearTaskLocked(newR, launchFlags); + } finally { + mStackSupervisor.endDeferResume(); + mReuseTask = false; + } return result; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 68445f6970fb..dbbb7ff69b3b 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.app.ActivityManager.TaskSnapshot; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RecordingCanvas; @@ -82,7 +81,7 @@ class TaskSnapshotController { /** * Return value for {@link #getSnapshotMode}: We are not allowed to take a real screenshot but - * we should try to use the app theme to create a dummy representation of the app. + * we should try to use the app theme to create a fake representation of the app. */ @VisibleForTesting static final int SNAPSHOT_MODE_APP_THEME = 1; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b33a8e9ef5df..7624d4c1ea36 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3612,7 +3612,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation"); - // TODO: Modify this to use the surface trace once it is not going crazy. + // TODO: Modify this to use the surface trace once it is not going baffling. // b/31532461 // TODO(multi-display): support multiple displays if (mStrictModeFlash == null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ebbd74aa7d1e..49e623d8dd11 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2456,9 +2456,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP finishInputEvent(event, true); } } - /** - * Dummy event receiver for windows that died visible. - */ + /** Fake event receiver for windows that died visible. */ private DeadWindowEventReceiver mDeadWindowEventReceiver; void openInputChannel(InputChannel outInputChannel) { @@ -2476,9 +2474,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClientChannel.dispose(); mClientChannel = null; } else { - // If the window died visible, we setup a dummy input channel, so that taps + // If the window died visible, we setup a fake input channel, so that taps // can still detected by input monitor channel, and we can relaunch the app. - // Create dummy event receiver that simply reports all events as handled. + // Create fake event receiver that simply reports all events as handled. mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel); } mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 16edb55939a3..92177abbbf85 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -560,9 +560,9 @@ class WindowStateAnimator { } } - // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values. - // This doesn't necessarily mean that there is an error in the system. The sizes might be - // incorrect, because it is before the first layout or draw. + // Something is wrong and SurfaceFlinger will not like this, try to revert to reasonable + // values. This doesn't necessarily mean that there is an error in the system. The sizes + // might be incorrect, because it is before the first layout or draw. if (outSize.width() < 1) { outSize.right = 1; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f7cd37fb6748..bc4d9a973ecb 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -548,7 +548,7 @@ class WindowToken extends WindowContainer<WindowState> { void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config) { if (mFixedRotationTransformState != null) { - return; + cleanUpFixedRotationTransformState(true /* replacing */); } mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, new Configuration(config), mDisplayContent.getRotation()); @@ -565,13 +565,13 @@ class WindowToken extends WindowContainer<WindowState> { * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { - if (mFixedRotationTransformState != null) { - return; - } final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; - if (fixedRotationState == null) { + if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) { return; } + if (mFixedRotationTransformState != null) { + cleanUpFixedRotationTransformState(true /* replacing */); + } mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); onConfigurationChanged(getParent().getConfiguration()); @@ -626,11 +626,17 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); + state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState( + false /* replacing */); } } - private void cleanUpFixedRotationTransformState() { + private void cleanUpFixedRotationTransformState(boolean replacing) { + if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) { + // The state is not only used by self. Make sure to leave the influence by others. + mFixedRotationTransformState.mAssociatedTokens.remove(this); + mFixedRotationTransformState.mRotatedContainers.remove(this); + } mFixedRotationTransformState = null; notifyFixedRotationTransform(false /* enabled */); } diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java deleted file mode 100644 index f6c854e23494..000000000000 --- a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.server.slice; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import androidx.test.filters.SmallTest; - -import com.android.server.UiServiceTestCase; -import com.android.server.slice.SliceManagerService.PackageMatchingCache; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class PackageMatchingCacheTest extends UiServiceTestCase { - - private final Supplier<String> supplier = mock(Supplier.class); - private final PackageMatchingCache cache = new PackageMatchingCache(supplier); - - @Test - public void testNulls() { - // Doesn't get for a null input - cache.matches(null); - verify(supplier, never()).get(); - - // Gets once valid input in sent. - cache.matches(""); - verify(supplier).get(); - } - - @Test - public void testCaching() { - when(supplier.get()).thenReturn("ret.pkg"); - - assertTrue(cache.matches("ret.pkg")); - assertTrue(cache.matches("ret.pkg")); - assertTrue(cache.matches("ret.pkg")); - - verify(supplier, times(1)).get(); - } - - @Test - public void testGetOnFailure() { - when(supplier.get()).thenReturn("ret.pkg"); - assertTrue(cache.matches("ret.pkg")); - - when(supplier.get()).thenReturn("other.pkg"); - assertTrue(cache.matches("other.pkg")); - verify(supplier, times(2)).get(); - } -} diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index a4436951f48b..cf1c36c0d243 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -90,8 +90,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testAddPinCreatesPinned() throws RemoteException { - doReturn("pkg").when(mService).getDefaultHome(anyInt()); - mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString()); @@ -99,8 +97,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase { @Test public void testRemovePinDestroysPinned() throws RemoteException { - doReturn("pkg").when(mService).getDefaultHome(anyInt()); - mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken); when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index d9c48fc4ba37..e0785c13a336 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -145,29 +146,40 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(0, token.getWindowsCount()); } - @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER }) @Test public void testFinishFixedRotationTransform() { - final WindowToken appToken = mAppWindow.mToken; - final WindowToken wallpaperToken = mWallpaperWindow.mToken; + final WindowToken[] tokens = new WindowToken[3]; + for (int i = 0; i < tokens.length; i++) { + tokens[i] = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); + } + final Configuration config = new Configuration(mDisplayContent.getConfiguration()); final int originalRotation = config.windowConfiguration.getRotation(); final int targetRotation = (originalRotation + 1) % 4; config.windowConfiguration.setRotation(targetRotation); - appToken.applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); - wallpaperToken.linkFixedRotationTransform(appToken); + tokens[0].applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + tokens[1].linkFixedRotationTransform(tokens[0]); // The window tokens should apply the rotation by the transformation. - assertEquals(targetRotation, appToken.getWindowConfiguration().getRotation()); - assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); + assertEquals(targetRotation, tokens[0].getWindowConfiguration().getRotation()); + assertEquals(targetRotation, tokens[1].getWindowConfiguration().getRotation()); + + tokens[2].applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + // The tokens[1] was linked to tokens[0], this should make tokens[1] link to tokens[2]. + tokens[1].linkFixedRotationTransform(tokens[2]); + + // Assume the display doesn't rotate, the transformation will be canceled. + tokens[0].finishFixedRotationTransform(); - // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.finishFixedRotationTransform(); + // The tokens[0] should restore to the original rotation. + assertEquals(originalRotation, tokens[0].getWindowConfiguration().getRotation()); + // The tokens[1] is linked to tokens[2], it should keep the target rotation. + assertNotEquals(originalRotation, tokens[1].getWindowConfiguration().getRotation()); - // The window tokens should restore to the original rotation. - assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation()); - assertEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); + tokens[2].finishFixedRotationTransform(); + // The rotation of tokens[1] should be restored because its linked state is finished. + assertEquals(originalRotation, tokens[1].getWindowConfiguration().getRotation()); } /** diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 2b2608724e12..78dc377a303c 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -46,6 +46,7 @@ import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.ISms; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.SmsRawData; +import com.android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1953,7 +1954,6 @@ public final class SmsManager { public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, @android.telephony.SmsCbMessage.MessageFormat int ranType) { boolean success = false; - if (endMessageId < startMessageId) { throw new IllegalArgumentException("endMessageId < startMessageId"); } @@ -1962,10 +1962,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.enableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "enableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling enableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "enableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } @@ -2019,10 +2023,14 @@ public final class SmsManager { if (iSms != null) { // If getSubscriptionId() returns INVALID or an inactive subscription, we will use // the default phone internally. - success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), + int subId = getSubscriptionId(); + success = iSms.disableCellBroadcastRangeForSubscriber(subId, startMessageId, endMessageId, ranType); + Rlog.d(TAG, "disableCellBroadcastRange: " + (success ? "succeeded" : "failed") + + " at calling disableCellBroadcastRangeForSubscriber. subId = " + subId); } } catch (RemoteException ex) { + Rlog.d(TAG, "disableCellBroadcastRange: " + ex.getStackTrace()); // ignore it } |