summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IUriGrantsManager.aidl3
-rw-r--r--core/java/android/view/HandwritingInitiator.java34
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java21
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp13
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java21
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java19
-rw-r--r--packages/CarrierDefaultApp/res/values-sq/strings.xml4
-rw-r--r--packages/CredentialManager/res/values-sq/strings.xml2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt33
-rw-r--r--packages/SystemUI/ktfmt_includes.txt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java110
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java42
16 files changed, 326 insertions, 53 deletions
diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl
index 9e7f2fecfea0..b630d034dca9 100644
--- a/core/java/android/app/IUriGrantsManager.aidl
+++ b/core/java/android/app/IUriGrantsManager.aidl
@@ -39,4 +39,7 @@ interface IUriGrantsManager {
void clearGrantedUriPermissions(in String packageName, int userId);
ParceledListSlice getUriPermissions(in String packageName, boolean incoming,
boolean persistedOnly);
+
+ int checkGrantUriPermission_ignoreNonSystem(
+ int sourceUid, String targetPkg, in Uri uri, int modeFlags, int userId);
}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 297754f7a5fd..dfade0167e22 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -81,6 +82,8 @@ public class HandwritingInitiator {
private int mConnectionCount = 0;
private final InputMethodManager mImm;
+ private final int[] mTempLocation = new int[2];
+
private final Rect mTempRect = new Rect();
private final RectF mTempRectF = new RectF();
@@ -429,7 +432,19 @@ public class HandwritingInitiator {
return null;
}
- private static void requestFocusWithoutReveal(View view) {
+ private void requestFocusWithoutReveal(View view) {
+ if (view instanceof EditText editText && !mState.mStylusDownWithinEditorBounds) {
+ // If the stylus down point was inside the EditText's bounds, then the EditText will
+ // automatically set its cursor position nearest to the stylus down point when it
+ // gains focus. If the stylus down point was outside the EditText's bounds (within
+ // the extended handwriting bounds), then we must calculate and set the cursor
+ // position manually.
+ view.getLocationInWindow(mTempLocation);
+ int offset = editText.getOffsetForPosition(
+ mState.mStylusDownX - mTempLocation[0],
+ mState.mStylusDownY - mTempLocation[1]);
+ editText.setSelection(offset);
+ }
if (view.getRevealOnFocusHint()) {
view.setRevealOnFocusHint(false);
view.requestFocus();
@@ -457,6 +472,10 @@ public class HandwritingInitiator {
if (getViewHandwritingArea(connectedView, handwritingArea)
&& isInHandwritingArea(handwritingArea, x, y, connectedView, isHover)
&& shouldTriggerStylusHandwritingForView(connectedView)) {
+ if (!isHover && mState != null) {
+ mState.mStylusDownWithinEditorBounds =
+ contains(handwritingArea, x, y, 0f, 0f, 0f, 0f);
+ }
return connectedView;
}
}
@@ -475,7 +494,12 @@ public class HandwritingInitiator {
}
final float distance = distance(handwritingArea, x, y);
- if (distance == 0f) return view;
+ if (distance == 0f) {
+ if (!isHover && mState != null) {
+ mState.mStylusDownWithinEditorBounds = true;
+ }
+ return view;
+ }
if (distance < minDistance) {
minDistance = distance;
bestCandidate = view;
@@ -658,6 +682,12 @@ public class HandwritingInitiator {
private boolean mExceedHandwritingSlop;
/**
+ * Whether the stylus down point of the MotionEvent sequence was within the editor's bounds
+ * (not including the extended handwriting bounds).
+ */
+ private boolean mStylusDownWithinEditorBounds;
+
+ /**
* A view which has requested focus and is pending input connection creation. When an input
* connection is created for the view, a handwriting session should be started for the view.
*/
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 1e56598d4e24..3ad49afb1575 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,12 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.UriGrantsManager;
+import android.content.ContentProvider;
+import android.content.Intent;
import android.graphics.RectF;
+import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignalBeamer;
@@ -37,6 +42,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
@@ -1193,7 +1199,22 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public void commitContent(InputConnectionCommandHeader header,
InputContentInfo inputContentInfo, int flags, Bundle opts,
AndroidFuture future /* T=Boolean */) {
+ final int imeUid = Binder.getCallingUid();
dispatchWithTracing("commitContent", future, () -> {
+ // Check if the originator IME has the right permissions
+ try {
+ final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(
+ inputContentInfo.getContentUri(), UserHandle.getUserId(imeUid));
+ final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(
+ inputContentInfo.getContentUri());
+ UriGrantsManager.getService().checkGrantUriPermission_ignoreNonSystem(imeUid, null,
+ contentUriWithoutUserId, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ contentUriOwnerUserId);
+ } catch (Exception e) {
+ Log.w(TAG, "commitContent with invalid Uri permission from IME:", e);
+ return false;
+ }
+
if (header.mSessionId != mCurrentSessionId.get()) {
return false; // cancelled
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c368fa85c379..56066b2d813c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1806,15 +1806,10 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
if (!is_system_server && getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
- if (rc == -ESRCH) {
- // If process is dead, treat this as a non-fatal error
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
- } else {
- fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
- "CONFIG_CGROUP_CPUACCT?")
- : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
- /* pid= */ 0, strerror(-rc)));
- }
+ fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
+ "CONFIG_CGROUP_CPUACCT?")
+ : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
+ /* pid= */ 0, strerror(-rc)));
}
}
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index c46118db617f..f39bddd7f032 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -88,8 +88,8 @@ public class HandwritingInitiatorTest {
}
private HandwritingInitiator mHandwritingInitiator;
- private View mTestView1;
- private View mTestView2;
+ private EditText mTestView1;
+ private EditText mTestView2;
private Context mContext;
@Before
@@ -123,6 +123,9 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
+ mTestView1.setText("hello");
+ when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);
+
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
@@ -141,6 +144,9 @@ public class HandwritingInitiatorTest {
// After IMM.startHandwriting is triggered, onTouchEvent should return true for ACTION_MOVE
// events so that the events are not dispatched to the view tree.
assertThat(onTouchEventResult2).isTrue();
+ // Since the stylus down point was inside the TextView's bounds, the handwriting initiator
+ // does not need to set the cursor position.
+ verify(mTestView1, never()).setSelection(anyInt());
}
@Test
@@ -185,6 +191,9 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_startHandwriting_when_stylusMove_withinExtendedHWArea() {
+ mTestView1.setText("hello");
+ when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);
+
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
final int x1 = sHwArea1.left - HW_BOUNDS_OFFSETS_LEFT_PX / 2;
final int y1 = sHwArea1.top - HW_BOUNDS_OFFSETS_TOP_PX / 2;
@@ -199,6 +208,9 @@ public class HandwritingInitiatorTest {
// Stylus movement within extended HandwritingArea should trigger IMM.startHandwriting once.
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView1);
+ // Since the stylus down point was outside the TextView's bounds, the handwriting initiator
+ // sets the cursor position.
+ verify(mTestView1).setSelection(4);
}
@Test
@@ -221,6 +233,8 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() {
+ mTestView1.setText("hello");
+ when(mTestView1.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(4);
// The stylus down point is between mTestView1 and mTestView2, but it is within the
// extended handwriting area of both views. It is closer to mTestView1.
final int x1 = sHwArea1.right + HW_BOUNDS_OFFSETS_RIGHT_PX / 2;
@@ -241,6 +255,9 @@ public class HandwritingInitiatorTest {
// the stylus down point is closest to this view.
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
verify(mHandwritingInitiator).startHandwriting(mTestView1);
+ // Since the stylus down point was outside the TextView's bounds, the handwriting initiator
+ // sets the cursor position.
+ verify(mTestView1).setSelection(4);
}
@Test
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
index b4c72ca3226b..3b2ab4c8bb50 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
@@ -16,6 +16,9 @@
package android.view.stylus;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -26,22 +29,23 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.EditText;
import androidx.test.platform.app.InstrumentationRegistry;
public class HandwritingTestUtil {
- public static View createView(Rect handwritingArea) {
+ public static EditText createView(Rect handwritingArea) {
return createView(handwritingArea, true /* autoHandwritingEnabled */,
true /* isStylusHandwritingAvailable */);
}
- public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
+ public static EditText createView(Rect handwritingArea, boolean autoHandwritingEnabled,
boolean isStylusHandwritingAvailable) {
return createView(handwritingArea, autoHandwritingEnabled, isStylusHandwritingAvailable,
0, 0, 0, 0);
}
- public static View createView(Rect handwritingArea, boolean autoHandwritingEnabled,
+ public static EditText createView(Rect handwritingArea, boolean autoHandwritingEnabled,
boolean isStylusHandwritingAvailable,
float handwritingBoundsOffsetLeft, float handwritingBoundsOffsetTop,
float handwritingBoundsOffsetRight, float handwritingBoundsOffsetBottom) {
@@ -68,7 +72,7 @@ public class HandwritingTestUtil {
}
};
- View view = spy(new View(context));
+ EditText view = spy(new EditText(context));
when(view.isAttachedToWindow()).thenReturn(true);
when(view.isAggregatedVisible()).thenReturn(true);
when(view.isStylusHandwritingAvailable()).thenReturn(isStylusHandwritingAvailable);
@@ -77,6 +81,13 @@ public class HandwritingTestUtil {
when(view.getHandwritingBoundsOffsetTop()).thenReturn(handwritingBoundsOffsetTop);
when(view.getHandwritingBoundsOffsetRight()).thenReturn(handwritingBoundsOffsetRight);
when(view.getHandwritingBoundsOffsetBottom()).thenReturn(handwritingBoundsOffsetBottom);
+ doAnswer(invocation -> {
+ int[] outLocation = invocation.getArgument(0);
+ outLocation[0] = handwritingArea.left;
+ outLocation[1] = handwritingArea.top;
+ return null;
+ }).when(view).getLocationInWindow(any());
+ when(view.getOffsetForPosition(anyFloat(), anyFloat())).thenReturn(0);
view.setAutoHandwritingEnabled(autoHandwritingEnabled);
parent.addView(view);
return view;
diff --git a/packages/CarrierDefaultApp/res/values-sq/strings.xml b/packages/CarrierDefaultApp/res/values-sq/strings.xml
index 238921aaa8f0..d8a1ed7f6620 100644
--- a/packages/CarrierDefaultApp/res/values-sq/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sq/strings.xml
@@ -5,7 +5,7 @@
<string name="android_system_label" msgid="2797790869522345065">"Operatori celular"</string>
<string name="portal_notification_id" msgid="5155057562457079297">"Të dhënat celulare kanë përfunduar"</string>
<string name="no_data_notification_id" msgid="668400731803969521">"Të dhënat celulare janë çaktivizuar"</string>
- <string name="portal_notification_detail" msgid="2295729385924660881">"Trokit për të vizituar sajtin e uebit të %s"</string>
+ <string name="portal_notification_detail" msgid="2295729385924660881">"Trokit për të vizituar uebsajtin e %s"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Kontakto me ofruesin e shërbimit %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Nuk ka lidhje të të dhënave celulare"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Shto plan të dhënash ose plan roaming përmes %s"</string>
@@ -16,7 +16,7 @@
<string name="ssl_error_continue" msgid="1138548463994095584">"Vazhdo gjithsesi nëpërmjet shfletuesit"</string>
<string name="performance_boost_notification_channel" msgid="3475440855635538592">"Përforcimi i performancës"</string>
<string name="performance_boost_notification_title" msgid="3126203390685781861">"Opsionet 5G nga operatori yt celular"</string>
- <string name="performance_boost_notification_detail" msgid="216569851036236346">"Vizito sajtin e uebit të %s për të parë opsione për përvojën tënde me aplikacionin"</string>
+ <string name="performance_boost_notification_detail" msgid="216569851036236346">"Vizito uebsajtin e %s për të parë opsione për përvojën tënde me aplikacionin"</string>
<string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Jo tani"</string>
<string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Menaxho"</string>
<string name="slice_purchase_app_label" msgid="7170191659233241166">"Bli një paketë përforcimi të performancës."</string>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
index 40f8dc48c5d5..bf6bc8bbbac9 100644
--- a/packages/CredentialManager/res/values-sq/strings.xml
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -34,7 +34,7 @@
<string name="public_key_cryptography_title" msgid="6751970819265298039">"Kriptografia e çelësit publik"</string>
<string name="public_key_cryptography_detail" msgid="6937631710280562213">"Bazuar në aleancën FIDO (e cila përfshin Google, Apple, Microsoft e të tjera) dhe standardet W3C, çelësat e kalimit përdorin çifte çelësash kriptografikë. Ndryshe nga emri i përdoruesit dhe vargu i karaktereve që përdorim për fjalëkalime, një çift çelësash privat-publik krijohet për aplikacion ose sajtin e uebit. Çelësi privat ruhet i sigurt në pajisjen tënde ose në menaxherin e fjalëkalimeve dhe konfirmon identitetin tënd. Çelësi publik ndahet me aplikacionin ose serverin e sajtit të uebit. Me çelësat përkatës, mund të regjistrohesh dhe të identifikohesh në çast."</string>
<string name="improved_account_security_title" msgid="1069841917893513424">"Siguri e përmirësuar e llogarisë"</string>
- <string name="improved_account_security_detail" msgid="9123750251551844860">"Secili çelës është i lidhur ekskluzivisht me aplikacionin ose sajtin e uebit për të cilin është krijuar, kështu që nuk do të identifikohesh asnjëherë gabimisht në një aplikacion ose sajt uebi mashtrues. Gjithashtu, me serverët që mbajnë vetëm çelësa publikë, pirateria informatike është shumë më e vështirë."</string>
+ <string name="improved_account_security_detail" msgid="9123750251551844860">"Secili çelës është i lidhur ekskluzivisht me aplikacionin ose uebsajtin për të cilin është krijuar, kështu që nuk do të identifikohesh asnjëherë gabimisht në një aplikacion ose uebsajt mashtrues. Gjithashtu, me serverët që mbajnë vetëm çelësa publikë, pirateria informatike është shumë më e vështirë."</string>
<string name="seamless_transition_title" msgid="5335622196351371961">"Kalim i thjeshtuar"</string>
<string name="seamless_transition_detail" msgid="4475509237171739843">"Teksa shkojmë drejt një të ardhmeje pa fjalëkalime, këto të fundit do të ofrohen ende së bashku me çelësat e kalimit."</string>
<string name="choose_provider_title" msgid="8870795677024868108">"Zgjidh se ku t\'i ruash <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 764a8556a54d..a95ab5565ab9 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -24,6 +24,7 @@ import android.graphics.Matrix
import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
+import android.os.Build
import android.os.Looper
import android.os.RemoteException
import android.util.Log
@@ -88,6 +89,9 @@ class ActivityLaunchAnimator(
contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
)
+ // TODO(b/288507023): Remove this flag.
+ @JvmField val DEBUG_LAUNCH_ANIMATION = Build.IS_DEBUGGABLE
+
private val DEFAULT_LAUNCH_ANIMATOR = LaunchAnimator(TIMINGS, INTERPOLATORS)
private val DEFAULT_DIALOG_TO_APP_ANIMATOR = LaunchAnimator(DIALOG_TIMINGS, INTERPOLATORS)
@@ -240,8 +244,17 @@ class ActivityLaunchAnimator(
private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
- this.launchContainer.context.mainExecutor.execute { this.onIntentStarted(willAnimate) }
+ this.launchContainer.context.mainExecutor.execute {
+ callOnIntentStartedOnMainThread(willAnimate)
+ }
} else {
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onIntentStarted(willAnimate=$willAnimate) " +
+ "[controller=$this]"
+ )
+ }
this.onIntentStarted(willAnimate)
}
}
@@ -542,6 +555,13 @@ class ActivityLaunchAnimator(
Log.i(TAG, "Aborting the animation as no window is opening")
removeTimeout()
iCallback?.invoke()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationCancelled() [no window opening]"
+ )
+ }
controller.onLaunchAnimationCancelled()
return
}
@@ -759,6 +779,10 @@ class ActivityLaunchAnimator(
Log.i(TAG, "Remote animation timed out")
timedOut = true
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
+ }
controller.onLaunchAnimationCancelled()
}
@@ -773,6 +797,13 @@ class ActivityLaunchAnimator(
removeTimeout()
animation?.cancel()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationCancelled() [remote animation cancelled]",
+ )
+ }
controller.onLaunchAnimationCancelled()
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 31df2baec08c..2d5796eedc78 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -303,7 +303,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 798f2d586bab..b4de914ce812 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -36,6 +36,7 @@ import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
@@ -288,7 +289,7 @@ public class NotificationShadeWindowViewController {
}
if (mExpandAnimationRunning) {
if (isDown && mClock.uptimeMillis() > mLaunchAnimationTimeout) {
- mShadeLogger.d("NSWVC: launch animation timed out");
+ Log.wtf(TAG, "NSWVC: launch animation timed out");
setExpandAnimationRunning(false);
} else {
return logDownDispatch(ev, "expand animation running", false);
@@ -545,6 +546,10 @@ public class NotificationShadeWindowViewController {
@VisibleForTesting
void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
+ // TODO(b/288507023): Remove this log.
+ if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
+ }
if (running) {
mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 91547a44c5f5..a08cd3bd869c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification
+import android.util.Log
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -30,6 +31,8 @@ import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.max
+private const val TAG = "NotificationLaunchAnimatorController"
+
/** A provider of [NotificationLaunchAnimatorController]. */
@CentralSurfacesComponent.CentralSurfacesScope
class NotificationLaunchAnimatorControllerProvider @Inject constructor(
@@ -89,28 +92,33 @@ class NotificationLaunchAnimatorController(
val clipStartLocation = notificationListContainer.topClippingStartLocation
val roundedTopClipping = (clipStartLocation - location[1]).coerceAtLeast(0)
val windowTop = location[1] + roundedTopClipping
- val topCornerRadius = if (roundedTopClipping > 0) {
- // Because the rounded Rect clipping is complex, we start the top rounding at
- // 0, which is pretty close to matching the real clipping.
- // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
- // if we'd like to have this perfect, but this is close enough.
- 0f
- } else {
- notification.topCornerRadius
- }
- val params = LaunchAnimationParameters(
- top = windowTop,
- bottom = location[1] + height,
- left = location[0],
- right = location[0] + notification.width,
- topCornerRadius = topCornerRadius,
- bottomCornerRadius = notification.bottomCornerRadius
- )
+ val topCornerRadius =
+ if (roundedTopClipping > 0) {
+ // Because the rounded Rect clipping is complex, we start the top rounding at
+ // 0, which is pretty close to matching the real clipping.
+ // We'd have to clipOut the overlaid drawable too with the outer rounded rect in
+ // case
+ // if we'd like to have this perfect, but this is close enough.
+ 0f
+ } else {
+ notification.topCornerRadius
+ }
+ val params =
+ LaunchAnimationParameters(
+ top = windowTop,
+ bottom = location[1] + height,
+ left = location[0],
+ right = location[0] + notification.width,
+ topCornerRadius = topCornerRadius,
+ bottomCornerRadius = notification.bottomCornerRadius
+ )
params.startTranslationZ = notification.translationZ
params.startNotificationTop = location[1]
- params.notificationParentTop = notificationListContainer
- .getViewParentForNotification(notificationEntry).locationOnScreen[1]
+ params.notificationParentTop =
+ notificationListContainer
+ .getViewParentForNotification(notificationEntry)
+ .locationOnScreen[1]
params.startRoundedTopClipping = roundedTopClipping
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
@@ -135,6 +143,9 @@ class NotificationLaunchAnimatorController(
}
override fun onIntentStarted(willAnimate: Boolean) {
+ if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
+ }
notificationExpansionRepository.setIsExpandAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
@@ -165,6 +176,10 @@ class NotificationLaunchAnimatorController(
}
override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "onLaunchAnimationCancelled()")
+ }
+
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationExpansionRepository.setIsExpandAnimationRunning(false)
@@ -177,11 +192,13 @@ class NotificationLaunchAnimatorController(
notification.isExpandAnimationRunning = true
notificationListContainer.setExpandingNotification(notification)
- jankMonitor.begin(notification,
- InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+ jankMonitor.begin(notification, InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "onLaunchAnimationEnd()")
+ }
jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
notification.isExpandAnimationRunning = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
index f605bdffbfd9..6f0a97adb311 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.notification.data.repository
+import android.util.Log
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+private const val TAG = "NotificationExpansionRepository"
+
/** A repository tracking the status of notification expansion animations. */
@SysUISingleton
class NotificationExpansionRepository @Inject constructor() {
@@ -37,6 +41,9 @@ class NotificationExpansionRepository @Inject constructor() {
/** Sets whether the notification expansion animation is currently running. */
fun setIsExpandAnimationRunning(running: Boolean) {
+ if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+ Log.d(TAG, "setIsExpandAnimationRunning(running=$running)")
+ }
_isExpandAnimationRunning.value = running
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e2a759..e1c4aad66559 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -177,7 +177,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
if (!activated) {
- clearAndTransitionToStateDetecting();
+ // cancel the magnification shortcut
+ mDetectingState.setShortcutTriggered(false);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index bf3f63c1ed30..d2737e81c55f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -539,6 +539,62 @@ public class AudioDeviceBroker {
}
}
}
+
+ // check playback or record activity after 6 seconds for UIDs
+ private static final int CHECK_CLIENT_STATE_DELAY_MS = 6000;
+
+ /*package */
+ void postCheckCommunicationRouteClientState(int uid, boolean wasActive, int delay) {
+ CommunicationRouteClient client = getCommunicationRouteClientForUid(uid);
+ if (client != null) {
+ sendMsgForCheckClientState(MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE,
+ SENDMSG_REPLACE,
+ uid,
+ wasActive ? 1 : 0,
+ client,
+ delay);
+ }
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ void onCheckCommunicationRouteClientState(int uid, boolean wasActive) {
+ CommunicationRouteClient client = getCommunicationRouteClientForUid(uid);
+ if (client == null) {
+ return;
+ }
+ updateCommunicationRouteClientState(client, wasActive);
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ /*package*/ void updateCommunicationRouteClientState(
+ CommunicationRouteClient client, boolean wasActive) {
+ boolean wasBtScoRequested = isBluetoothScoRequested();
+ client.setPlaybackActive(mAudioService.isPlaybackActiveForUid(client.getUid()));
+ client.setRecordingActive(mAudioService.isRecordingActiveForUid(client.getUid()));
+ if (wasActive != client.isActive()) {
+ postUpdateCommunicationRouteClient(
+ wasBtScoRequested, "updateCommunicationRouteClientState");
+ }
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ /*package*/ void setForceCommunicationClientStateAndDelayedCheck(
+ CommunicationRouteClient client,
+ boolean forcePlaybackActive,
+ boolean forceRecordingActive) {
+ if (client == null) {
+ return;
+ }
+ if (forcePlaybackActive) {
+ client.setPlaybackActive(true);
+ }
+ if (forceRecordingActive) {
+ client.setRecordingActive(true);
+ }
+ postCheckCommunicationRouteClientState(
+ client.getUid(), client.isActive(), CHECK_CLIENT_STATE_DELAY_MS);
+ }
+
/* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
AudioDeviceInfo[] allDevices =
@@ -1901,6 +1957,12 @@ public class AudioDeviceBroker {
case MSG_PERSIST_AUDIO_DEVICE_SETTINGS:
onPersistAudioDeviceSettings();
break;
+
+ case MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE: {
+ synchronized (mDeviceStateLock) {
+ onCheckCommunicationRouteClientState(msg.arg1, msg.arg2 == 1);
+ }
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1982,6 +2044,8 @@ public class AudioDeviceBroker {
private static final int MSG_L_RECEIVED_BT_EVENT = 55;
+ private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -2095,6 +2159,23 @@ public class AudioDeviceBroker {
}
}
+ private void removeMsgForCheckClientState(int uid) {
+ CommunicationRouteClient crc = getCommunicationRouteClientForUid(uid);
+ if (crc != null) {
+ mBrokerHandler.removeEqualMessages(MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE, crc);
+ }
+ }
+
+ private void sendMsgForCheckClientState(int msg, int existingMsgPolicy,
+ int arg1, int arg2, Object obj, int delay) {
+ if ((existingMsgPolicy == SENDMSG_REPLACE) && (obj != null)) {
+ mBrokerHandler.removeEqualMessages(msg, obj);
+ }
+
+ long time = SystemClock.uptimeMillis() + delay;
+ mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj), time);
+ }
+
/** List of messages for which music is muted while processing is pending */
private static final Set<Integer> MESSAGES_MUTE_MUSIC;
static {
@@ -2367,6 +2448,7 @@ public class AudioDeviceBroker {
if (unregister) {
cl.unregisterDeathRecipient();
}
+ removeMsgForCheckClientState(cl.getUid());
mCommunicationRouteClients.remove(cl);
return cl;
}
@@ -2383,6 +2465,13 @@ public class AudioDeviceBroker {
new CommunicationRouteClient(cb, uid, device, isPrivileged);
if (client.registerDeathRecipient()) {
mCommunicationRouteClients.add(0, client);
+ if (!client.isActive()) {
+ // initialize the inactive client's state as active and check it after 6 seconds
+ setForceCommunicationClientStateAndDelayedCheck(
+ client,
+ !mAudioService.isPlaybackActiveForUid(client.getUid()),
+ !mAudioService.isRecordingActiveForUid(client.getUid()));
+ }
return client;
}
return null;
@@ -2439,16 +2528,16 @@ public class AudioDeviceBroker {
List<AudioRecordingConfiguration> recordConfigs) {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- final boolean wasBtScoRequested = isBluetoothScoRequested();
- boolean updateCommunicationRoute = false;
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
boolean wasActive = crc.isActive();
+ boolean updateClientState = false;
if (playbackConfigs != null) {
crc.setPlaybackActive(false);
for (AudioPlaybackConfiguration config : playbackConfigs) {
if (config.getClientUid() == crc.getUid()
&& config.isActive()) {
crc.setPlaybackActive(true);
+ updateClientState = true;
break;
}
}
@@ -2459,18 +2548,23 @@ public class AudioDeviceBroker {
if (config.getClientUid() == crc.getUid()
&& !config.isClientSilenced()) {
crc.setRecordingActive(true);
+ updateClientState = true;
break;
}
}
}
- if (wasActive != crc.isActive()) {
- updateCommunicationRoute = true;
+ if (updateClientState) {
+ removeMsgForCheckClientState(crc.getUid());
+ updateCommunicationRouteClientState(crc, wasActive);
+ } else {
+ if (wasActive) {
+ setForceCommunicationClientStateAndDelayedCheck(
+ crc,
+ playbackConfigs != null /* forcePlaybackActive */,
+ recordConfigs != null /* forceRecordingActive */);
+ }
}
}
- if (updateCommunicationRoute) {
- postUpdateCommunicationRouteClient(
- wasBtScoRequested, "updateCommunicationRouteClientsActivity");
- }
}
}
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 01fdc8800c0e..7862f58374a3 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -41,6 +41,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -62,6 +63,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -1304,6 +1306,46 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
return false;
}
+ /**
+ * Check if the targetPkg can be granted permission to access uri by
+ * the callingUid using the given modeFlags. See {@link #checkGrantUriPermissionUnlocked}.
+ *
+ * @param callingUid The uid of the grantor app that has permissions to the uri.
+ * @param targetPkg The package name of the granted app that needs permissions to the uri.
+ * @param uri The uri for which permissions should be granted.
+ * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+ * @param userId The userId in which the uri is to be resolved.
+ * @return uid of the target or -1 if permission grant not required. Returns -1 if the caller
+ * does not hold INTERACT_ACROSS_USERS_FULL
+ * @throws SecurityException if the grant is not allowed.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ public int checkGrantUriPermission_ignoreNonSystem(int callingUid, String targetPkg, Uri uri,
+ int modeFlags, int userId) {
+ if (!isCallerIsSystemOrPrivileged()) {
+ return Process.INVALID_UID;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return checkGrantUriPermissionUnlocked(callingUid, targetPkg, uri, modeFlags,
+ userId);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private boolean isCallerIsSystemOrPrivileged() {
+ final int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
+ return true;
+ }
+ return ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ uid, /* owningUid = */-1, /* exported = */ true)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public ArrayList<UriPermission> providePersistentUriGrants() {
final ArrayList<UriPermission> result = new ArrayList<>();