Merge "Temp skip /product folder during root-hash calculation." into tm-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d6a067d..2c0b6e9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -800,6 +800,7 @@
method public boolean hasRequestForegroundServiceExemption();
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
+ method public void setEnableOnBackInvokedCallback(boolean);
field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
field public int privateFlags;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c7c654a..3b1943b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2537,8 +2537,8 @@
* restriction} for a certain app-op.
*/
private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
- null, //COARSE_LOCATION
- null, //FINE_LOCATION
+ new RestrictionBypass(true, false, false), //COARSE_LOCATION
+ new RestrictionBypass(true, false, false), //FINE_LOCATION
null, //GPS
null, //VIBRATE
null, //READ_CONTACTS
@@ -2547,7 +2547,7 @@
null, //WRITE_CALL_LOG
null, //READ_CALENDAR
null, //WRITE_CALENDAR
- new RestrictionBypass(true, false), //WIFI_SCAN
+ new RestrictionBypass(false, true, false), //WIFI_SCAN
null, //POST_NOTIFICATION
null, //NEIGHBORING_CELLS
null, //CALL_PHONE
@@ -2561,10 +2561,10 @@
null, //READ_ICC_SMS
null, //WRITE_ICC_SMS
null, //WRITE_SETTINGS
- new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
+ new RestrictionBypass(false, true, false), //SYSTEM_ALERT_WINDOW
null, //ACCESS_NOTIFICATIONS
null, //CAMERA
- new RestrictionBypass(false, true), //RECORD_AUDIO
+ new RestrictionBypass(false, false, true), //RECORD_AUDIO
null, //PLAY_AUDIO
null, //READ_CLIPBOARD
null, //WRITE_CLIPBOARD
@@ -2582,7 +2582,7 @@
null, //MONITOR_HIGH_POWER_LOCATION
null, //GET_USAGE_STATS
null, //MUTE_MICROPHONE
- new RestrictionBypass(true, false), //TOAST_WINDOW
+ new RestrictionBypass(false, true, false), //TOAST_WINDOW
null, //PROJECT_MEDIA
null, //ACTIVATE_VPN
null, //WALLPAPER
@@ -2614,7 +2614,7 @@
null, // ACCEPT_HANDOVER
null, // MANAGE_IPSEC_HANDOVERS
null, // START_FOREGROUND
- new RestrictionBypass(true, false), // BLUETOOTH_SCAN
+ new RestrictionBypass(false, true, false), // BLUETOOTH_SCAN
null, // USE_BIOMETRIC
null, // ACTIVITY_RECOGNITION
null, // SMS_FINANCIAL_TRANSACTIONS
@@ -3331,6 +3331,9 @@
* @hide
*/
public static class RestrictionBypass {
+ /** Does the app need to be system uid to bypass the restriction */
+ public boolean isSystemUid;
+
/** Does the app need to be privileged to bypass the restriction */
public boolean isPrivileged;
@@ -3340,12 +3343,14 @@
*/
public boolean isRecordAudioRestrictionExcept;
- public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) {
+ public RestrictionBypass(boolean isSystemUid, boolean isPrivileged,
+ boolean isRecordAudioRestrictionExcept) {
+ this.isSystemUid = isSystemUid;
this.isPrivileged = isPrivileged;
this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
}
- public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true);
+ public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(false, true, true);
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 315bd71..0a2b421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1811,10 +1811,6 @@
* #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
* #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
*
- * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
- * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
- * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
- *
* <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}. In
* that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have
* the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -1834,6 +1830,10 @@
* activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
* contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}.
*
+ * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+ * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+ * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+ *
* <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. In
* that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have the
* {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -13249,8 +13249,9 @@
* Called by a device owner, profile owner of a managed profile or delegated app with
* {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
*
- * <p> When network logging is enabled by a profile owner, the network logs will only include
- * work profile network activity, not activity on the personal profile.
+ * <p> Supported for a device owner from Android 8. Supported for a profile owner of a managed
+ * profile from Android 12. When network logging is enabled by a profile owner, the network logs
+ * will only include work profile network activity, not activity on the personal profile.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2961b55..24c3836 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2752,4 +2752,21 @@
mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
}
}
+
+ /**
+ * Sets whether the application will use the {@link android.window.OnBackInvokedCallback}
+ * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related
+ * callbacks. Intended to be used from tests only.
+ *
+ * @see #isOnBackInvokedCallbackEnabled()
+ * @hide
+ */
+ @TestApi
+ public void setEnableOnBackInvokedCallback(boolean isEnable) {
+ if (isEnable) {
+ privateFlagsExt |= PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+ } else {
+ privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+ }
+ }
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f9ed0e3d..3f49b73 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -37,6 +37,7 @@
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
@@ -194,7 +195,9 @@
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
final IBinder startInputToken = (IBinder) args.arg1;
- final IInputContext inputContext = (IInputContext) args.arg2;
+ final IInputContext inputContext = (IInputContext) ((SomeArgs) args.arg2).arg1;
+ final ImeOnBackInvokedDispatcher imeDispatcher =
+ (ImeOnBackInvokedDispatcher) ((SomeArgs) args.arg2).arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
@@ -205,7 +208,7 @@
: null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
- navButtonFlags);
+ navButtonFlags, imeDispatcher);
args.recycle();
return;
}
@@ -348,13 +351,17 @@
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
EditorInfo attribute, boolean restarting,
- @InputMethodNavButtonFlags int navButtonFlags) {
+ @InputMethodNavButtonFlags int navButtonFlags,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = inputContext;
+ args.arg2 = imeDispatcher;
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
+ args, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index efd4f06..25296bc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -101,6 +101,7 @@
import android.view.Choreographer;
import android.view.Gravity;
import android.view.InputChannel;
+import android.view.InputDevice;
import android.view.InputEventReceiver;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -134,6 +135,9 @@
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
@@ -344,6 +348,9 @@
* A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
**/
private RingBuffer<MotionEvent> mPendingEvents;
+ private ImeOnBackInvokedDispatcher mImeDispatcher;
+ private Boolean mBackCallbackRegistered = false;
+ private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
/**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -797,7 +804,13 @@
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ mImeDispatcher = imeDispatcher;
+ if (mWindow != null) {
+ mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher(
+ imeDispatcher);
+ }
mPrivOps.reportStartInputAsync(startInputToken);
mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
if (restarting) {
@@ -1496,6 +1509,10 @@
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
+ if (mImeDispatcher != null) {
+ mWindow.getOnBackInvokedDispatcher()
+ .setImeOnBackInvokedDispatcher(mImeDispatcher);
+ }
mNavigationBarController.onSoftInputWindowCreated(mWindow);
{
final Window window = mWindow.getWindow();
@@ -1608,6 +1625,8 @@
// when IME developers are doing something unsupported.
InputMethodPrivilegedOperationsRegistry.remove(mToken);
}
+ unregisterCompatOnBackInvokedCallback();
+ mImeDispatcher = null;
}
/**
@@ -2568,9 +2587,47 @@
cancelImeSurfaceRemoval();
mInShowWindow = false;
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ registerCompatOnBackInvokedCallback();
}
+ /**
+ * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
+ * back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code
+ * around to handle back on older devices.
+ */
+ private void registerCompatOnBackInvokedCallback() {
+ if (mBackCallbackRegistered) {
+ return;
+ }
+ if (mWindow != null) {
+ mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback);
+ mBackCallbackRegistered = true;
+ }
+ }
+
+ private void unregisterCompatOnBackInvokedCallback() {
+ if (!mBackCallbackRegistered) {
+ return;
+ }
+ if (mWindow != null) {
+ mWindow.getOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mCompatBackCallback);
+ mBackCallbackRegistered = false;
+ }
+ }
+
+ private KeyEvent createBackKeyEvent(int action, boolean isTracking) {
+ final long when = SystemClock.uptimeMillis();
+ return new KeyEvent(when, when, action,
+ KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+ | (isTracking ? KeyEvent.FLAG_TRACKING : 0),
+ InputDevice.SOURCE_KEYBOARD);
+ }
+
private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
mDecorViewVisible = true;
@@ -2658,6 +2715,7 @@
}
mLastWasInFullscreenMode = mIsFullscreen;
updateFullscreenMode();
+ unregisterCompatOnBackInvokedCallback();
}
/**
@@ -3797,4 +3855,14 @@
proto.end(token);
}
};
+
+ private void compatHandleBack() {
+ final KeyEvent downEvent = createBackKeyEvent(
+ KeyEvent.ACTION_DOWN, false /* isTracking */);
+ onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
+ final boolean hasStartedTracking =
+ (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+ final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
+ onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
+ }
}
diff --git a/core/java/android/service/quickaccesswallet/TEST_MAPPING b/core/java/android/service/quickaccesswallet/TEST_MAPPING
index 4d97ab6..5d2a3a8 100644
--- a/core/java/android/service/quickaccesswallet/TEST_MAPPING
+++ b/core/java/android/service/quickaccesswallet/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsQuickAccessWalletTestCases"
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2475e2c..d04b07c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9922,7 +9922,7 @@
* <ol>
* <li>It should only be called when content capture is enabled for the view.
* <li>It must call viewAppeared() before viewDisappeared()
- * <li>viewAppearead() can only be called when the view is visible and laidout
+ * <li>viewAppeared() can only be called when the view is visible and laid out
* <li>It should not call the same event twice.
* </ol>
*/
@@ -9999,6 +9999,11 @@
Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
}
}
+
+ // We reset any translation state as views may be re-used (e.g., as in ListView and
+ // RecyclerView). We only need to do this for views important for content capture since
+ // views unimportant for content capture won't be translated anyway.
+ clearTranslationState();
}
}
@@ -12719,6 +12724,21 @@
}
/**
+ * @hide
+ */
+ public void clearTranslationState() {
+ if (mViewTranslationCallback != null) {
+ mViewTranslationCallback.onClearTranslation(this);
+ }
+ clearViewTranslationCallback();
+ clearViewTranslationResponse();
+ if (hasTranslationTransientState()) {
+ setHasTransientState(false);
+ setHasTranslationTransientState(false);
+ }
+ }
+
+ /**
* Returns true if this view is currently attached to a window.
*/
public boolean isAttachedToWindow() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0bdbfbc..48c102b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6095,6 +6095,28 @@
@Override
protected int onProcess(QueuedInputEvent q) {
+ if (q.mEvent instanceof KeyEvent) {
+ final KeyEvent event = (KeyEvent) q.mEvent;
+
+ // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
+ // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
+ if (isBack(event)
+ && mContext != null
+ && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+ OnBackInvokedCallback topCallback =
+ getOnBackInvokedDispatcher().getTopCallback();
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (topCallback != null) {
+ topCallback.onBackInvoked();
+ return FINISH_HANDLED;
+ }
+ } else {
+ // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
+ return FINISH_NOT_HANDLED;
+ }
+ }
+ }
+
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
@@ -6446,24 +6468,6 @@
return FINISH_HANDLED;
}
- // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
- // view tree and invoke the appropriate {@link OnBackInvokedCallback}.
- if (isBack(event)
- && mContext != null
- && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
- OnBackInvokedCallback topCallback =
- getOnBackInvokedDispatcher().getTopCallback();
- if (event.getAction() == KeyEvent.ACTION_UP) {
- if (topCallback != null) {
- topCallback.onBackInvoked();
- return FINISH_HANDLED;
- }
- } else {
- // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
- return FINISH_NOT_HANDLED;
- }
- }
-
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6209b46..dbdc0da 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -29,6 +29,7 @@
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.View;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -232,6 +233,10 @@
* long as your implementation of {@link InputMethod} relies on such
* IPCs
* @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
+ * @param imeDispatcher The {@link ImeOnBackInvokedDispatcher }} to be set on the
+ * IME's {@link android.window.WindowOnBackInvokedDispatcher}, so that IME
+ * {@link android.window.OnBackInvokedCallback}s can be forwarded to
+ * the client requesting to start input.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -240,7 +245,8 @@
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+ @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d9bde58..e2e9a85 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -91,6 +91,8 @@
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
@@ -105,6 +107,7 @@
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.os.SomeArgs;
+import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
@@ -279,6 +282,21 @@
private static final String SUBTYPE_MODE_VOICE = "voice";
/**
+ * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
+ * int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive
+ * {@link android.window.OnBackInvokedCallback} registrations from IME.
+ */
+ private final ImeOnBackInvokedDispatcher mImeDispatcher =
+ new ImeOnBackInvokedDispatcher(Handler.getMain()) {
+ @Override
+ public WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+ synchronized (mH) {
+ return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null;
+ }
+ }
+ };
+
+ /**
* Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
* or indirectly relied on {@link #sInstance} via reflection or something like that.
*
@@ -740,7 +758,8 @@
windowFlags,
null,
null, null,
- mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+ mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
+ mImeDispatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1687,6 +1706,8 @@
mServedConnecting = false;
clearConnectionLocked();
}
+ // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
+ mImeDispatcher.clear();
}
public void displayCompletions(View view, CompletionInfo[] completions) {
@@ -2359,7 +2380,8 @@
softInputMode, windowFlags, tba, servedInputConnection,
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
- view.getContext().getApplicationInfo().targetSdkVersion);
+ view.getContext().getApplicationInfo().targetSdkVersion,
+ mImeDispatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8cf032b..6bf2474 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -158,12 +158,7 @@
case STATE_UI_TRANSLATION_FINISHED:
destroyTranslators();
runForEachView((view, callback) -> {
- callback.onClearTranslation(view);
- view.clearViewTranslationResponse();
- if (view.hasTranslationTransientState()) {
- view.setHasTransientState(false);
- view.setHasTranslationTransientState(false);
- }
+ view.clearTranslationState();
});
notifyTranslationFinished(/* activityDestroyed= */ false);
synchronized (mLock) {
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.aidl b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
new file mode 100644
index 0000000..04e6420
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/ParcelFileDescriptor.aidl
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.window;
+
+parcelable ImeOnBackInvokedDispatcher;
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
new file mode 100644
index 0000000..d5763aa
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A {@link OnBackInvokedDispatcher} for IME that forwards {@link OnBackInvokedCallback}
+ * registrations from the IME process to the app process to be registered on the app window.
+ * <p>
+ * The app process creates and propagates an instance of {@link ImeOnBackInvokedDispatcher}
+ * to the IME to be set on the IME window's {@link WindowOnBackInvokedDispatcher}.
+ * <p>
+ * @see WindowOnBackInvokedDispatcher#setImeOnBackInvokedDispatcher
+ *
+ * @hide
+ */
+public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parcelable {
+
+ private static final String TAG = "ImeBackDispatcher";
+ static final String RESULT_KEY_ID = "id";
+ static final String RESULT_KEY_CALLBACK = "callback";
+ static final String RESULT_KEY_PRIORITY = "priority";
+ static final int RESULT_CODE_REGISTER = 0;
+ static final int RESULT_CODE_UNREGISTER = 1;
+ @NonNull
+ private final ResultReceiver mResultReceiver;
+
+ public ImeOnBackInvokedDispatcher(Handler handler) {
+ mResultReceiver = new ResultReceiver(handler) {
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ WindowOnBackInvokedDispatcher dispatcher = getReceivingDispatcher();
+ if (dispatcher != null) {
+ receive(resultCode, resultData, dispatcher);
+ }
+ }
+ };
+ }
+
+ /**
+ * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window
+ * that should receive the forwarded callback.
+ */
+ @Nullable
+ protected WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+ return null;
+ }
+
+ ImeOnBackInvokedDispatcher(Parcel in) {
+ mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
+ }
+
+ @Override
+ public void registerOnBackInvokedCallback(
+ @OnBackInvokedDispatcher.Priority int priority,
+ @NonNull OnBackInvokedCallback callback) {
+ final Bundle bundle = new Bundle();
+ final IOnBackInvokedCallback iCallback =
+ new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback);
+ bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder());
+ bundle.putInt(RESULT_KEY_PRIORITY, priority);
+ bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+ mResultReceiver.send(RESULT_CODE_REGISTER, bundle);
+ }
+
+ @Override
+ public void unregisterOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+ mResultReceiver.send(RESULT_CODE_UNREGISTER, bundle);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mResultReceiver, flags);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<ImeOnBackInvokedDispatcher> CREATOR =
+ new Parcelable.Creator<ImeOnBackInvokedDispatcher>() {
+ public ImeOnBackInvokedDispatcher createFromParcel(Parcel in) {
+ return new ImeOnBackInvokedDispatcher(in);
+ }
+ public ImeOnBackInvokedDispatcher[] newArray(int size) {
+ return new ImeOnBackInvokedDispatcher[size];
+ }
+ };
+
+ private final HashMap<Integer, OnBackInvokedCallback> mImeCallbackMap = new HashMap<>();
+
+ private void receive(
+ int resultCode, Bundle resultData,
+ @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+ final int callbackId = resultData.getInt(RESULT_KEY_ID);
+ if (resultCode == RESULT_CODE_REGISTER) {
+ int priority = resultData.getInt(RESULT_KEY_PRIORITY);
+ final IOnBackInvokedCallback callback = IOnBackInvokedCallback.Stub.asInterface(
+ resultData.getBinder(RESULT_KEY_CALLBACK));
+ registerReceivedCallback(
+ callback, priority, callbackId, receivingDispatcher);
+ } else if (resultCode == RESULT_CODE_UNREGISTER) {
+ unregisterReceivedCallback(callbackId, receivingDispatcher);
+ }
+ }
+
+ private void registerReceivedCallback(
+ @NonNull IOnBackInvokedCallback iCallback,
+ @OnBackInvokedDispatcher.Priority int priority,
+ int callbackId,
+ @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+ final ImeOnBackInvokedCallback imeCallback =
+ new ImeOnBackInvokedCallback(iCallback);
+ mImeCallbackMap.put(callbackId, imeCallback);
+ receivingDispatcher.registerOnBackInvokedCallback(priority, imeCallback);
+ }
+
+ private void unregisterReceivedCallback(
+ int callbackId, OnBackInvokedDispatcher receivingDispatcher) {
+ final OnBackInvokedCallback callback = mImeCallbackMap.get(callbackId);
+ if (callback == null) {
+ Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
+ + "callbackId: " + callbackId);
+ return;
+ }
+ receivingDispatcher.unregisterOnBackInvokedCallback(callback);
+ }
+
+ /** Clears all registered callbacks on the instance. */
+ public void clear() {
+ mImeCallbackMap.clear();
+ }
+
+ static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
+ @NonNull
+ private final IOnBackInvokedCallback mIOnBackInvokedCallback;
+
+ ImeOnBackInvokedCallback(@NonNull IOnBackInvokedCallback iCallback) {
+ mIOnBackInvokedCallback = iCallback;
+ }
+
+ @Override
+ public void onBackInvoked() {
+ try {
+ if (mIOnBackInvokedCallback != null) {
+ mIOnBackInvokedCallback.onBackInvoked();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
+ }
+ }
+
+ IOnBackInvokedCallback getIOnBackInvokedCallback() {
+ return mIOnBackInvokedCallback;
+ }
+ }
+}
diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java
index 6bc2b50..3539049a 100644
--- a/core/java/android/window/OnBackInvokedDispatcher.java
+++ b/core/java/android/window/OnBackInvokedDispatcher.java
@@ -96,4 +96,19 @@
* @hide
*/
default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
+
+
+ /**
+ * Sets an {@link ImeOnBackInvokedDispatcher} to forward {@link OnBackInvokedCallback}s
+ * from IME to the app process to be registered on the app window.
+ *
+ * Only call this on the IME window. Create the {@link ImeOnBackInvokedDispatcher} from
+ * the application process and override
+ * {@link ImeOnBackInvokedDispatcher#getReceivingDispatcher()} to point to the app
+ * window's {@link WindowOnBackInvokedDispatcher}.
+ *
+ * @hide
+ */
+ default void setImeOnBackInvokedDispatcher(
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { }
}
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 4409397..bedf503 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -49,6 +49,7 @@
private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>();
private final Object mLock = new Object();
private OnBackInvokedDispatcher mActualDispatcher = null;
+ private ImeOnBackInvokedDispatcher mImeDispatcher;
@Override
public void registerOnBackInvokedCallback(
@@ -108,6 +109,9 @@
Log.v(TAG, String.format("Proxy transferring %d callbacks to %s", mCallbacks.size(),
mActualDispatcher));
}
+ if (mImeDispatcher != null) {
+ mActualDispatcher.setImeOnBackInvokedDispatcher(mImeDispatcher);
+ }
for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
int priority = callbackPair.second;
if (priority >= 0) {
@@ -117,6 +121,7 @@
}
}
mCallbacks.clear();
+ mImeDispatcher = null;
}
private void clearCallbacksOnDispatcher() {
@@ -142,6 +147,7 @@
}
synchronized (mLock) {
mCallbacks.clear();
+ mImeDispatcher = null;
}
}
@@ -169,4 +175,14 @@
transferCallbacksToDispatcher();
}
}
+
+ @Override
+ public void setImeOnBackInvokedDispatcher(
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ if (mActualDispatcher != null) {
+ mActualDispatcher.setImeOnBackInvokedDispatcher(imeDispatcher);
+ } else {
+ mImeDispatcher = imeDispatcher;
+ }
+ }
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 781859c..edfdbcc 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -55,6 +55,8 @@
.getInt("persist.wm.debug.predictive_back", 1) != 0;
private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
.getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+ @Nullable
+ private ImeOnBackInvokedDispatcher mImeDispatcher;
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -94,6 +96,10 @@
private void registerOnBackInvokedCallbackUnchecked(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
+ return;
+ }
if (!mOnBackInvokedCallbacks.containsKey(priority)) {
mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
}
@@ -120,6 +126,10 @@
@Override
public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ if (mImeDispatcher != null) {
+ mImeDispatcher.unregisterOnBackInvokedCallback(callback);
+ return;
+ }
if (!mAllCallbacks.containsKey(callback)) {
if (DEBUG) {
Log.i(TAG, "Callback not found. returning...");
@@ -153,6 +163,9 @@
}
mAllCallbacks.clear();
mOnBackInvokedCallbacks.clear();
+ if (mImeDispatcher != null) {
+ mImeDispatcher = null;
+ }
}
private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -160,14 +173,18 @@
return;
}
try {
- if (callback == null) {
- mWindowSession.setOnBackInvokedCallbackInfo(mWindow, null);
- } else {
+ OnBackInvokedCallbackInfo callbackInfo = null;
+ if (callback != null) {
int priority = mAllCallbacks.get(callback);
- mWindowSession.setOnBackInvokedCallbackInfo(
- mWindow, new OnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackWrapper(callback), priority));
+ final IOnBackInvokedCallback iCallback =
+ callback instanceof ImeOnBackInvokedDispatcher
+ .ImeOnBackInvokedCallback
+ ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
+ callback).getIOnBackInvokedCallback()
+ : new OnBackInvokedCallbackWrapper(callback);
+ callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
}
+ mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
if (DEBUG && callback == null) {
Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s",
Debug.getCallers(5, " ")));
@@ -190,7 +207,7 @@
return null;
}
- private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+ static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
private final WeakReference<OnBackInvokedCallback> mCallback;
OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
@@ -270,4 +287,10 @@
return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
}
+
+ @Override
+ public void setImeOnBackInvokedDispatcher(
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ mImeDispatcher = imeDispatcher;
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3fee914..781b6d5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -971,7 +971,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_COPY,
"",
- -1);
+ -1,
+ false);
setResult(RESULT_OK);
finish();
@@ -1155,7 +1156,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_NEARBY,
"",
- -1);
+ -1,
+ false);
// Action bar is user-independent, always start as primary
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
finish();
@@ -1177,7 +1179,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_EDIT,
"",
- -1);
+ -1,
+ false);
// Action bar is user-independent, always start as primary
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
finish();
@@ -1754,7 +1757,8 @@
target.getComponentName().getPackageName()
+ target.getTitle().toString(),
mMaxHashSaltDays);
- directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo);
+ SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo;
+ directTargetAlsoRanked = getRankedPosition(selectableTargetInfo);
if (mCallerChooserTargets != null) {
numCallerProvided = mCallerChooserTargets.length;
@@ -1762,7 +1766,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_SERVICE,
targetInfo.getResolveInfo().activityInfo.processName,
- value
+ value,
+ selectableTargetInfo.isPinned()
);
break;
case ChooserListAdapter.TARGET_CALLER:
@@ -1773,7 +1778,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_APP,
targetInfo.getResolveInfo().activityInfo.processName,
- value
+ value,
+ targetInfo.isPinned()
);
break;
case ChooserListAdapter.TARGET_STANDARD_AZ:
@@ -1784,7 +1790,8 @@
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_STANDARD,
targetInfo.getResolveInfo().activityInfo.processName,
- value
+ value,
+ false
);
break;
}
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index 3217307..bb7d50a 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -34,7 +34,8 @@
int appProvidedApp, boolean isWorkprofile, int previewType, String intent);
/** Logs a UiEventReported event for the system sharesheet when the user selects a target. */
- void logShareTargetSelected(int targetType, String packageName, int positionPicked);
+ void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+ boolean isPinned);
/** Logs a UiEventReported event for the system sharesheet being triggered by the user. */
default void logSharesheetTriggered() {
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
index 48bdba3..e3cc4f1 100644
--- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -51,12 +51,14 @@
}
@Override
- public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+ public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+ boolean isPinned) {
FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
/* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(),
/* package_name = 2 */ packageName,
/* instance_id = 3 */ getInstanceId().getId(),
- /* position_picked = 4 */ positionPicked);
+ /* position_picked = 4 */ positionPicked,
+ /* is_pinned = 5 */ isPinned);
}
@Override
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index c5b21ac..e7f80a7 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -589,7 +589,7 @@
MetricsLogger metricsLogger = new MetricsLogger();
LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
log.setComponentName(mRankerServiceName);
- log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed);
+ log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed ? 1 : 0);
log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
metricsLogger.write(log);
}
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 983e0fe..ffbf646 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -69,7 +69,8 @@
/* event_id = 1 */ eventID,
/* package_name = 2 */ packageName,
/* instance_id = 3 */ 0,
- /* position_picked = 4 */ position);
+ /* position_picked = 4 */ position,
+ /* is_pinned = 5 */ false);
}
}
@@ -82,7 +83,8 @@
/* event_id = 1 */ eventID,
/* package_name = 2 */ packageName,
/* instance_id = 3 */ instance.getId(),
- /* position_picked = 4 */ position);
+ /* position_picked = 4 */ position,
+ /* is_pinned = 5 */ false);
} else if ((eventID > 0)) {
logWithPosition(event, uid, packageName, position);
}
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 397b2c0..62dea9d 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -137,6 +137,18 @@
*/
public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
int maxHeight) {
+ return buildScaledBitmap(drawable, maxWidth, maxHeight, false);
+ }
+
+ /**
+ * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+ *
+ * @param allowUpscaling if true, the drawable will not only be scaled down, but also scaled up
+ * to fit within the maximum size given. This is useful for converting
+ * vectorized icons which usually have a very small intrinsic size.
+ */
+ public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+ int maxHeight, boolean allowUpscaling) {
if (drawable == null) {
return null;
}
@@ -155,7 +167,9 @@
// a large notification icon if necessary
float ratio = Math.min((float) maxWidth / (float) originalWidth,
(float) maxHeight / (float) originalHeight);
- ratio = Math.min(1.0f, ratio);
+ if (!allowUpscaling) {
+ ratio = Math.min(1.0f, ratio);
+ }
int scaledWidth = (int) (ratio * originalWidth);
int scaledHeight = (int) (ratio * originalHeight);
Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 40d89db..4e2526a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
@@ -47,7 +48,8 @@
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
- in EditorInfo attribute, boolean restarting, int navigationBarFlags);
+ in EditorInfo attribute, boolean restarting, int navigationBarFlags,
+ in ImeOnBackInvokedDispatcher imeDispatcher);
void onNavButtonFlagsChanged(int navButtonFlags);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index d7bb2cb..3157760 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -20,6 +20,7 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
@@ -57,7 +58,7 @@
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion);
+ int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
index 2ecc261..0dca638 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
@@ -44,6 +44,7 @@
// share completed fields
public int targetType;
public int positionPicked;
+ public boolean isPinned;
CallRecord(int atomId, UiEventLogger.UiEventEnum eventId,
String packageName, InstanceId instanceId) {
@@ -68,12 +69,13 @@
}
CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType,
- int positionPicked) {
+ int positionPicked, boolean isPinned) {
this.atomId = atomId;
this.packageName = packageName;
this.instanceId = instanceId;
this.targetType = targetType;
this.positionPicked = positionPicked;
+ this.isPinned = isPinned;
}
}
@@ -112,9 +114,11 @@
}
@Override
- public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+ public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+ boolean isPinned) {
mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(),
- SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked));
+ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked,
+ isPinned));
}
@Override
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0e8388b..12d3d64 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2581,12 +2581,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
- "397105698": {
- "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
- "level": "VERBOSE",
- "group": "WM_DEBUG_FOCUS",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"397382873": {
"message": "Moving to PAUSED: %s %s",
"level": "VERBOSE",
@@ -3115,6 +3109,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "958338552": {
+ "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"959486822": {
"message": "setSyncGroup #%d on %s",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
index 13b4096..54f8808 100644
--- a/data/keyboards/Vendor_0957_Product_0001.kl
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -44,33 +44,37 @@
key 11 0
# custom keys
-key usage 0x000c019C PROFILE_SWITCH
-key usage 0x000c01A2 ALL_APPS
key usage 0x000c01BB TV_INPUT
-key usage 0x000c022A BOOKMARK
-key usage 0x000c0096 SETTINGS
-key usage 0x000c009F NOTIFICATION
-key usage 0x000c008D GUIDE
-key usage 0x000c0089 TV
-key usage 0x000c009C CHANNEL_UP
-key usage 0x000c009D CHANNEL_DOWN
-key usage 0x000c00CD MEDIA_PLAY_PAUSE
-key usage 0x000c00B2 MEDIA_RECORD
-key usage 0x000c00B4 MEDIA_SKIP_BACKWARD
-key usage 0x000c00B3 MEDIA_SKIP_FORWARD
-key usage 0x000c0226 MEDIA_STOP
-key usage 0x000c0077 BUTTON_3 WAKE #YouTube
-key usage 0x000c0078 BUTTON_4 WAKE #Netflix
-key usage 0x000c0079 BUTTON_6 WAKE #Disney+
-key usage 0x000c007A BUTTON_7 WAKE #HBOmax
-
-key usage 0x00070037 PERIOD
-key usage 0x000c01BD INFO
-key usage 0x000c0061 CAPTIONS
key usage 0x000c0185 TV_TELETEXT
+key usage 0x000c0061 CAPTIONS
+
+key usage 0x000c01BD INFO
+key usage 0x000c0037 PERIOD
key usage 0x000c0069 PROG_RED
key usage 0x000c006A PROG_GREEN
+key usage 0x000c006C PROG_YELLOW
key usage 0x000c006B PROG_BLUE
-key usage 0x000c006C PROG_YELLOW
\ No newline at end of file
+key usage 0x000c00B4 MEDIA_SKIP_BACKWARD
+key usage 0x000c00CD MEDIA_PLAY_PAUSE
+key usage 0x000c00B2 MEDIA_RECORD
+key usage 0x000c00B3 MEDIA_SKIP_FORWARD
+
+key usage 0x000c022A BOOKMARK
+key usage 0x000c01A2 ALL_APPS
+key usage 0x000c019C PROFILE_SWITCH
+
+key usage 0x000c0096 SETTINGS
+key usage 0x000c009F NOTIFICATION
+
+key usage 0x000c008D GUIDE
+key usage 0x000c0089 TV
+
+key usage 0x000c009C CHANNEL_UP
+key usage 0x000c009D CHANNEL_DOWN
+
+key usage 0x000c0077 BUTTON_3 WAKE #YouTube
+key usage 0x000c0078 BUTTON_4 WAKE #Netflix
+key usage 0x000c0079 BUTTON_6 WAKE
+key usage 0x000c007A BUTTON_7 WAKE
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index c3fbe55..8fa9f56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -246,9 +246,13 @@
* {@link BackAnimationController}
*/
public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
- if (action == MotionEvent.ACTION_DOWN) {
- initAnimation(event);
- } else if (action == MotionEvent.ACTION_MOVE) {
+ if (action == MotionEvent.ACTION_MOVE) {
+ if (!mBackGestureStarted) {
+ // Let the animation initialized here to make sure the onPointerDownOutsideFocus
+ // could be happened when ACTION_DOWN, it may change the current focus that we
+ // would access it when startBackNavigation.
+ initAnimation(event);
+ }
onMove(event, swipeEdge);
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 145e527..dfd4362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -143,8 +143,11 @@
@Provides
static TvPipNotificationController provideTvPipNotificationController(Context context,
PipMediaController pipMediaController,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
+ TvPipBoundsState tvPipBoundsState,
@ShellMainThread Handler mainHandler) {
- return new TvPipNotificationController(context, pipMediaController, mainHandler);
+ return new TvPipNotificationController(context, pipMediaController,
+ pipParamsChangedForwarder, tvPipBoundsState, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 8a50f22..65a12d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Handler;
@@ -64,7 +65,7 @@
*/
public interface ActionListener {
/**
- * Called when the media actions changes.
+ * Called when the media actions changed.
*/
void onMediaActionsChanged(List<RemoteAction> actions);
}
@@ -74,11 +75,21 @@
*/
public interface MetadataListener {
/**
- * Called when the media metadata changes.
+ * Called when the media metadata changed.
*/
void onMediaMetadataChanged(MediaMetadata metadata);
}
+ /**
+ * A listener interface to receive notification on changes to the media session token.
+ */
+ public interface TokenListener {
+ /**
+ * Called when the media session token changed.
+ */
+ void onMediaSessionTokenChanged(MediaSession.Token token);
+ }
+
private final Context mContext;
private final Handler mMainHandler;
private final HandlerExecutor mHandlerExecutor;
@@ -133,6 +144,7 @@
private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
+ private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
@@ -204,6 +216,31 @@
mMetadataListeners.remove(listener);
}
+ /**
+ * Adds a new token listener.
+ */
+ public void addTokenListener(TokenListener listener) {
+ if (!mTokenListeners.contains(listener)) {
+ mTokenListeners.add(listener);
+ listener.onMediaSessionTokenChanged(getToken());
+ }
+ }
+
+ /**
+ * Removes a token listener.
+ */
+ public void removeTokenListener(TokenListener listener) {
+ listener.onMediaSessionTokenChanged(null);
+ mTokenListeners.remove(listener);
+ }
+
+ private MediaSession.Token getToken() {
+ if (mMediaController == null) {
+ return null;
+ }
+ return mMediaController.getSessionToken();
+ }
+
private MediaMetadata getMediaMetadata() {
return mMediaController != null ? mMediaController.getMetadata() : null;
}
@@ -294,6 +331,7 @@
}
notifyActionsChanged();
notifyMetadataChanged(getMediaMetadata());
+ notifyTokenChanged(getToken());
// TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
}
@@ -317,4 +355,10 @@
mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
}
}
+
+ private void notifyTokenChanged(MediaSession.Token token) {
+ if (!mTokenListeners.isEmpty()) {
+ mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index fcd1f95..7667794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -293,6 +293,8 @@
}
mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
mTvPipBoundsState.setTvPipExpanded(expanding);
+ mPipNotificationController.updateExpansionState();
+
updatePinnedStackBounds();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 4033f03..61a609d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -16,36 +16,47 @@
package com.android.wm.shell.pip.tv;
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.media.MediaMetadata;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
/**
- * A notification that informs users that PIP is running and also provides PIP controls.
- * <p>Once it's created, it will manage the PIP notification UI by itself except for handling
- * configuration changes.
+ * A notification that informs users that PiP is running and also provides PiP controls.
+ * <p>Once it's created, it will manage the PiP notification UI by itself except for handling
+ * configuration changes and user initiated expanded PiP toggling.
*/
public class TvPipNotificationController {
private static final String TAG = "TvPipNotification";
- private static final boolean DEBUG = TvPipController.DEBUG;
// Referenced in com.android.systemui.util.NotificationChannels.
public static final String NOTIFICATION_CHANNEL = "TVPIP";
@@ -60,6 +71,8 @@
"com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
private static final String ACTION_TOGGLE_EXPANDED_PIP =
"com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
+ private static final String ACTION_FULLSCREEN =
+ "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
private final Context mContext;
private final PackageManager mPackageManager;
@@ -68,44 +81,88 @@
private final ActionBroadcastReceiver mActionBroadcastReceiver;
private final Handler mMainHandler;
private Delegate mDelegate;
+ private final TvPipBoundsState mTvPipBoundsState;
private String mDefaultTitle;
+ private final List<RemoteAction> mCustomActions = new ArrayList<>();
+ private final List<RemoteAction> mMediaActions = new ArrayList<>();
+ private RemoteAction mCustomCloseAction;
+
+ private MediaSession.Token mMediaSessionToken;
+
/** Package name for the application that owns PiP window. */
private String mPackageName;
- private boolean mNotified;
- private String mMediaTitle;
- private Bitmap mArt;
+
+ private boolean mIsNotificationShown;
+ private String mPipTitle;
+ private String mPipSubtitle;
+
+ private Bitmap mActivityIcon;
public TvPipNotificationController(Context context, PipMediaController pipMediaController,
+ PipParamsChangedForwarder pipParamsChangedForwarder, TvPipBoundsState tvPipBoundsState,
Handler mainHandler) {
mContext = context;
mPackageManager = context.getPackageManager();
mNotificationManager = context.getSystemService(NotificationManager.class);
mMainHandler = mainHandler;
+ mTvPipBoundsState = tvPipBoundsState;
mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
.setLocalOnly(true)
- .setOngoing(false)
+ .setOngoing(true)
.setCategory(Notification.CATEGORY_SYSTEM)
.setShowWhen(true)
.setSmallIcon(R.drawable.pip_icon)
+ .setAllowSystemGeneratedContextualActions(false)
+ .setContentIntent(createPendingIntent(context, ACTION_FULLSCREEN))
+ .setDeleteIntent(getCloseAction().actionIntent)
.extend(new Notification.TvExtender()
.setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU))
.setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP)));
mActionBroadcastReceiver = new ActionBroadcastReceiver();
- pipMediaController.addMetadataListener(this::onMediaMetadataChanged);
+ pipMediaController.addActionListener(this::onMediaActionsChanged);
+ pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
+
+ pipParamsChangedForwarder.addListener(
+ new PipParamsChangedForwarder.PipParamsChangedCallback() {
+ @Override
+ public void onExpandedAspectRatioChanged(float ratio) {
+ updateExpansionState();
+ }
+
+ @Override
+ public void onActionsChanged(List<RemoteAction> actions,
+ RemoteAction closeAction) {
+ mCustomActions.clear();
+ mCustomActions.addAll(actions);
+ mCustomCloseAction = closeAction;
+ updateNotificationContent();
+ }
+
+ @Override
+ public void onTitleChanged(String title) {
+ mPipTitle = title;
+ updateNotificationContent();
+ }
+
+ @Override
+ public void onSubtitleChanged(String subtitle) {
+ mPipSubtitle = subtitle;
+ updateNotificationContent();
+ }
+ });
onConfigurationChanged(context);
}
void setDelegate(Delegate delegate) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setDelegate(), delegate=%s", TAG, delegate);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
+ TAG, delegate);
+
if (mDelegate != null) {
throw new IllegalStateException(
"The delegate has already been set and should not change.");
@@ -118,90 +175,181 @@
}
void show(String packageName) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
if (mDelegate == null) {
throw new IllegalStateException("Delegate is not set.");
}
+ mIsNotificationShown = true;
mPackageName = packageName;
- update();
+ mActivityIcon = getActivityIcon();
mActionBroadcastReceiver.register();
+
+ updateNotificationContent();
}
void dismiss() {
- mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
- mNotified = false;
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: dismiss()", TAG);
+
+ mIsNotificationShown = false;
mPackageName = null;
mActionBroadcastReceiver.unregister();
+
+ mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
}
- private void onMediaMetadataChanged(MediaMetadata metadata) {
- if (updateMediaControllerMetadata(metadata) && mNotified) {
- // update notification
- update();
+ private Notification.Action getToggleAction(boolean expanded) {
+ if (expanded) {
+ return createSystemAction(R.drawable.pip_ic_collapse,
+ R.string.pip_collapse, ACTION_TOGGLE_EXPANDED_PIP);
+ } else {
+ return createSystemAction(R.drawable.pip_ic_expand, R.string.pip_expand,
+ ACTION_TOGGLE_EXPANDED_PIP);
}
}
+ private Notification.Action createSystemAction(int iconRes, int titleRes, String action) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(
+ Icon.createWithResource(mContext, iconRes),
+ mContext.getString(titleRes),
+ createPendingIntent(mContext, action));
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+ private void onMediaActionsChanged(List<RemoteAction> actions) {
+ mMediaActions.clear();
+ mMediaActions.addAll(actions);
+ if (mCustomActions.isEmpty()) {
+ updateNotificationContent();
+ }
+ }
+
+ private void onMediaSessionTokenChanged(MediaSession.Token token) {
+ mMediaSessionToken = token;
+ updateNotificationContent();
+ }
+
+ private Notification.Action remoteToNotificationAction(RemoteAction action) {
+ return remoteToNotificationAction(action, SEMANTIC_ACTION_NONE);
+ }
+
+ private Notification.Action remoteToNotificationAction(RemoteAction action,
+ int semanticAction) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(action.getIcon(),
+ action.getTitle(),
+ action.getActionIntent());
+ if (action.getContentDescription() != null) {
+ Bundle extras = new Bundle();
+ extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
+ action.getContentDescription());
+ builder.addExtras(extras);
+ }
+ builder.setSemanticAction(semanticAction);
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+ private Notification.Action[] getNotificationActions() {
+ final List<Notification.Action> actions = new ArrayList<>();
+
+ // 1. Fullscreen
+ actions.add(getFullscreenAction());
+ // 2. Close
+ actions.add(getCloseAction());
+ // 3. App actions
+ final List<RemoteAction> appActions =
+ mCustomActions.isEmpty() ? mMediaActions : mCustomActions;
+ for (RemoteAction appAction : appActions) {
+ if (PipUtils.remoteActionsMatch(mCustomCloseAction, appAction)
+ || !appAction.isEnabled()) {
+ continue;
+ }
+ actions.add(remoteToNotificationAction(appAction));
+ }
+ // 4. Move
+ actions.add(getMoveAction());
+ // 5. Toggle expansion (if expanded PiP enabled)
+ if (mTvPipBoundsState.getDesiredTvExpandedAspectRatio() > 0
+ && mTvPipBoundsState.isTvExpandedPipSupported()) {
+ actions.add(getToggleAction(mTvPipBoundsState.isTvPipExpanded()));
+ }
+ return actions.toArray(new Notification.Action[0]);
+ }
+
+ private Notification.Action getCloseAction() {
+ if (mCustomCloseAction == null) {
+ return createSystemAction(R.drawable.pip_ic_close_white, R.string.pip_close,
+ ACTION_CLOSE_PIP);
+ } else {
+ return remoteToNotificationAction(mCustomCloseAction, SEMANTIC_ACTION_DELETE);
+ }
+ }
+
+ private Notification.Action getFullscreenAction() {
+ return createSystemAction(R.drawable.pip_ic_fullscreen_white,
+ R.string.pip_fullscreen, ACTION_FULLSCREEN);
+ }
+
+ private Notification.Action getMoveAction() {
+ return createSystemAction(R.drawable.pip_ic_move_white, R.string.pip_move,
+ ACTION_MOVE_PIP);
+ }
+
/**
- * Called by {@link PipController} when the configuration is changed.
+ * Called by {@link TvPipController} when the configuration is changed.
*/
void onConfigurationChanged(Context context) {
mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title);
- if (mNotified) {
- // Update the notification.
- update();
- }
+ updateNotificationContent();
}
- private void update() {
- mNotified = true;
+ void updateExpansionState() {
+ updateNotificationContent();
+ }
+
+ private void updateNotificationContent() {
+ if (mPackageManager == null || !mIsNotificationShown) {
+ return;
+ }
+
+ Notification.Action[] actions = getNotificationActions();
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: update(), title: %s, subtitle: %s, mediaSessionToken: %s, #actions: %s", TAG,
+ getNotificationTitle(), mPipSubtitle, mMediaSessionToken, actions.length);
+ for (Notification.Action action : actions) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: action: %s", TAG,
+ action.toString());
+ }
+
mNotificationBuilder
.setWhen(System.currentTimeMillis())
- .setContentTitle(getNotificationTitle());
- if (mArt != null) {
- mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
- .bigPicture(mArt));
- } else {
- mNotificationBuilder.setStyle(null);
- }
+ .setContentTitle(getNotificationTitle())
+ .setContentText(mPipSubtitle)
+ .setSubText(getApplicationLabel(mPackageName))
+ .setActions(actions);
+ setPipIcon();
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mMediaSessionToken);
+ mNotificationBuilder.setExtras(extras);
+
+ // TvExtender not recognized if not set last.
+ mNotificationBuilder.extend(new Notification.TvExtender()
+ .setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
+ .setDeleteIntent(createPendingIntent(mContext, ACTION_CLOSE_PIP)));
mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
mNotificationBuilder.build());
}
- private boolean updateMediaControllerMetadata(MediaMetadata metadata) {
- String title = null;
- Bitmap art = null;
- if (metadata != null) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
- }
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- if (art == null) {
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- }
- }
-
- if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) {
- return false;
- }
-
- mMediaTitle = title;
- mArt = art;
-
- return true;
- }
-
-
private String getNotificationTitle() {
- if (!TextUtils.isEmpty(mMediaTitle)) {
- return mMediaTitle;
+ if (!TextUtils.isEmpty(mPipTitle)) {
+ return mPipTitle;
}
-
final String applicationTitle = getApplicationLabel(mPackageName);
if (!TextUtils.isEmpty(applicationTitle)) {
return applicationTitle;
}
-
return mDefaultTitle;
}
@@ -214,10 +362,37 @@
}
}
+ private void setPipIcon() {
+ if (mActivityIcon != null) {
+ mNotificationBuilder.setLargeIcon(mActivityIcon);
+ return;
+ }
+ // Fallback: Picture-in-Picture icon
+ mNotificationBuilder.setLargeIcon(Icon.createWithResource(mContext, R.drawable.pip_icon));
+ }
+
+ private Bitmap getActivityIcon() {
+ if (mContext == null) return null;
+ ComponentName componentName = PipUtils.getTopPipActivity(mContext).first;
+ if (componentName == null) return null;
+
+ Drawable drawable;
+ try {
+ drawable = mPackageManager.getActivityIcon(componentName);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ int width = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ int height = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_height);
+ return ImageUtils.buildScaledBitmap(drawable, width, height, /* allowUpscaling */ true);
+ }
+
private static PendingIntent createPendingIntent(Context context, String action) {
return PendingIntent.getBroadcast(context, 0,
new Intent(action).setPackage(context.getPackageName()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
private class ActionBroadcastReceiver extends BroadcastReceiver {
@@ -228,6 +403,7 @@
mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
mIntentFilter.addAction(ACTION_MOVE_PIP);
mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
+ mIntentFilter.addAction(ACTION_FULLSCREEN);
}
boolean mRegistered = false;
@@ -249,10 +425,8 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: on(Broadcast)Receive(), action=%s", TAG, action);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: on(Broadcast)Receive(), action=%s", TAG, action);
if (ACTION_SHOW_PIP_MENU.equals(action)) {
mDelegate.showPictureInPictureMenu();
@@ -262,14 +436,21 @@
mDelegate.enterPipMovementMenu();
} else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
mDelegate.togglePipExpansion();
+ } else if (ACTION_FULLSCREEN.equals(action)) {
+ mDelegate.movePipToFullscreen();
}
}
}
interface Delegate {
void showPictureInPictureMenu();
+
void closePip();
+
void enterPipMovementMenu();
+
void togglePipExpansion();
+
+ void movePipToFullscreen();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6cf8829..42b1014 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -209,12 +209,11 @@
createNavigationInfo(animationTarget, null, null,
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
- // Check that back start is dispatched.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
- verify(mIOnBackInvokedCallback).onBackStarted();
- // Check that back progress is dispatched.
+ // Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+ verify(mIOnBackInvokedCallback).onBackStarted();
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture());
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 284da73..2f30baa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -234,5 +234,10 @@
if (!(mPreference instanceof RestrictedTopLevelPreference)) {
mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
}
+
+ if (mPreference instanceof PrimarySwitchPreference) {
+ ((PrimarySwitchPreference) mPreference)
+ .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+ }
}
}
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
index 528f603..fd06238 100644
--- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,88 +14,83 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.core.widget.NestedScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <com.android.internal.widget.AlertDialogLayout
- android:id="@*android:id/parentPanel"
+<com.android.internal.widget.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/dialog_top_padding"
+>
+
+ <include layout="@layout/alert_dialog_title_systemui" />
+
+ <FrameLayout
+ android:id="@*android:id/contentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_horizontal|top"
- android:orientation="vertical"
- android:paddingTop="@dimen/dialog_top_padding"
- >
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
- <include layout="@layout/alert_dialog_title_systemui" />
-
- <FrameLayout
- android:id="@*android:id/contentPanel"
+ <ScrollView
+ android:id="@*android:id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding"
- >
+ android:clipToPadding="false">
- <ScrollView
- android:id="@*android:id/scrollView"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clipToPadding="false">
+ android:orientation="vertical">
- <LinearLayout
+ <Space
+ android:id="@*android:id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+
+ <TextView
+ android:id="@*android:id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ style="@style/TextAppearance.Dialog.Body.Message" />
- <Space
- android:id="@*android:id/textSpacerNoTitle"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="0dp" />
+ <Space
+ android:id="@*android:id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="6dp" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
- <TextView
- android:id="@*android:id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.Dialog.Body.Message" />
-
- <Space
- android:id="@*android:id/textSpacerNoButtons"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="6dp" />
- </LinearLayout>
- </ScrollView>
- </FrameLayout>
+ <FrameLayout
+ android:id="@*android:id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding"
+ >
<FrameLayout
- android:id="@*android:id/customPanel"
+ android:id="@*android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/dialog_side_padding"
+ android:paddingEnd="@dimen/dialog_side_padding">
+ <include
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding"
- >
-
- <FrameLayout
- android:id="@*android:id/custom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/dialog_side_padding"
- android:paddingEnd="@dimen/dialog_side_padding">
- <include
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/alert_dialog_button_bar_systemui" />
- </FrameLayout>
- </com.android.internal.widget.AlertDialogLayout>
-
-</androidx.core.widget.NestedScrollView>
\ No newline at end of file
+ layout="@layout/alert_dialog_button_bar_systemui" />
+ </FrameLayout>
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
new file mode 100644
index 0000000..71bb938
--- /dev/null
+++ b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<androidx.core.widget.NestedScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <include layout="@layout/alert_dialog_systemui" />
+
+</androidx.core.widget.NestedScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f9e73ec..0c25f54 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -380,7 +380,7 @@
<item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:colorBackground">?androidprv:attr/colorSurface</item>
- <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+ <item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
<item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
</style>
@@ -389,6 +389,10 @@
<item name="android:layout">@layout/alert_dialog_systemui</item>
</style>
+ <style name="ScrollableAlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+ <item name="android:layout">@layout/scrollable_alert_dialog_systemui</item>
+ </style>
+
<style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog">
<item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item>
<item name="android:paddingBottom">@dimen/dialog_bottom_padding</item>
@@ -994,6 +998,7 @@
<style name="Theme.SystemUI.Dialog.Cast">
<item name="android:textAppearanceMedium">@style/TextAppearance.CastItem</item>
+ <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
</style>
<!-- ************************************************************************************* -->
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
rename to packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 5d154c3..4e48a52 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -38,7 +38,6 @@
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -80,8 +79,19 @@
import dagger.Provides;
/**
- * A dagger module for injecting default implementations of components of System UI that may be
- * overridden by the System UI implementation.
+ * A dagger module for injecting default implementations of components of System UI.
+ *
+ * Variants of SystemUI should make a copy of this, include it in their component, and customize it
+ * as needed.
+ *
+ * This module might alternatively be named `AospSystemUIModule`, `PhoneSystemUIModule`,
+ * or `BasicSystemUIModule`.
+ *
+ * Nothing in the module should be strictly required. Each piece should either be swappable with
+ * a different implementation or entirely removable.
+ *
+ * This is different from {@link SystemUIModule} which should be used for pieces of required
+ * SystemUI code that variants of SystemUI _must_ include to function correctly.
*/
@Module(includes = {
MediaModule.class,
@@ -90,7 +100,7 @@
StartCentralSurfacesModule.class,
VolumeModule.class
})
-public abstract class SystemUIDefaultModule {
+public abstract class ReferenceSystemUIModule {
@SysUISingleton
@Provides
@@ -101,9 +111,6 @@
}
@Binds
- abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
- @Binds
abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
@@ -148,6 +155,7 @@
return spC;
}
+ /** */
@Binds
@SysUISingleton
public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 701972a..5d34a69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -73,7 +73,7 @@
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
- SystemUIDefaultModule.class})
+ ReferenceSystemUIModule.class})
public interface SysUIComponent {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index bbeb66c..535eff8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -100,8 +100,14 @@
import dagger.Provides;
/**
- * A dagger module for injecting components of System UI that are not overridden by the System UI
- * implementation.
+ * A dagger module for injecting components of System UI that are required by System UI.
+ *
+ * If your feature can be excluded, subclassed, or re-implemented by a variant of SystemUI, put
+ * your Dagger Module in {@link ReferenceSystemUIModule} and/or any variant modules that
+ * rely on the feature.
+ *
+ * Adding an entry in this file means that _all_ variants of SystemUI will receive that code. They
+ * may not appreciate that.
*/
@Module(includes = {
AppOpsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 1437c96..7eccb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -278,13 +278,14 @@
/**
* Apply squishFraction to a copy of viewState such that the cached version is untouched.
*/
- private fun squishViewState(
+ @VisibleForTesting
+ internal fun squishViewState(
viewState: TransitionViewState,
squishFraction: Float
): TransitionViewState {
val squishedViewState = viewState.copy()
squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
- val albumArtViewState = viewState.widgetStates.get(R.id.album_art)
+ val albumArtViewState = squishedViewState.widgetStates.get(R.id.album_art)
if (albumArtViewState != null) {
albumArtViewState.height = squishedViewState.height
}
@@ -317,6 +318,7 @@
if (transitionLayout == null) {
return null
}
+
// Not cached. Let's create a new measurement
if (state.expansion == 0.0f || state.expansion == 1.0f) {
result = transitionLayout!!.calculateViewState(
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 8b8941a..3709a86 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -16,6 +16,8 @@
package com.android.systemui.power.dagger;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
@@ -28,5 +30,9 @@
public interface PowerModule {
/** */
@Binds
+ EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
+
+ /** */
+ @Binds
PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
}
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 985df42..7da3238 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -936,7 +937,7 @@
// the launcher icons animation starts, so use that as our
// duration.
.setDuration(unlockAnimationStartDelay)
- .setInterpolator(EMPHASIZED_DECELERATE)
+ .setInterpolator(EMPHASIZED_ACCELERATE)
.withEndAction(() -> {
instantCollapse();
mView.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index b7f90a4..4685c14 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -38,7 +38,6 @@
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -102,9 +101,6 @@
}
@Binds
- abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
- @Binds
abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index acff871..ebdddbf 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -180,7 +180,7 @@
int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
- mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever);
+ mQuickAccessWalletClient.getWalletCards(mCallbackExecutor, request, cardsRetriever);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
index 604e1f3..1817809 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
@@ -4,16 +4,22 @@
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.animation.WidgetState
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
/**
* Tests for {@link MediaViewController}.
@@ -31,6 +37,9 @@
private lateinit var mediaViewController: MediaViewController
private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
+ @Mock private lateinit var mockViewState: TransitionViewState
+ @Mock private lateinit var mockCopiedState: TransitionViewState
+ @Mock private lateinit var mockWidgetState: WidgetState
@Before
fun setUp() {
@@ -63,4 +72,15 @@
mediaHostStateHolder.squishFraction = 0.5f
assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50)
}
+
+ @Test
+ fun testSquish_DoesNotMutateViewState() {
+ whenever(mockViewState.copy()).thenReturn(mockCopiedState)
+ whenever(mockCopiedState.widgetStates)
+ .thenReturn(mutableMapOf(R.id.album_art to mockWidgetState))
+
+ mediaViewController.squishViewState(mockViewState, 0.5f)
+ verify(mockViewState, times(1)).copy()
+ verifyNoMoreInteractions(mockViewState)
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fc73a59..cceacd8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2792,6 +2792,15 @@
}
int N = procs.size();
+ for (int i = 0; i < N; ++i) {
+ final ProcessRecord proc = procs.get(i).first;
+ try {
+ Process.setProcessFrozen(proc.getPid(), proc.uid, true);
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName);
+ }
+ }
+
for (int i=0; i<N; i++) {
final Pair<ProcessRecord, Boolean> proc = procs.get(i);
removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3e97b91..36afb36 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4545,8 +4545,9 @@
* @return The restriction matching the package
*/
private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
- return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission(
- android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+ return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
+ mContext.checkPermission(android.Manifest.permission
+ .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
== PackageManager.PERMISSION_GRANTED);
}
@@ -4853,6 +4854,9 @@
if (opBypass != null) {
// If we are the system, bypass user restrictions for certain codes
synchronized (this) {
+ if (opBypass.isSystemUid && appBypass != null && appBypass.isSystemUid) {
+ return false;
+ }
if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
return false;
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 122a950..d8aa9aa 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -486,8 +486,7 @@
for (SADeviceState deviceState : mSADevices) {
if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
isInList = true;
if (forceEnable) {
deviceState.mEnabled = true;
@@ -511,8 +510,7 @@
for (SADeviceState deviceState : mSADevices) {
if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
deviceState.mEnabled = false;
break;
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5ae3f33..e4e9d1d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1712,6 +1712,13 @@
mPointerIconDisplayContext = null;
}
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ setPointerIconVisible(AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE,
+ displayId);
+ setPointerAcceleration(AdditionalDisplayInputProperties.DEFAULT_POINTER_ACCELERATION,
+ displayId);
+ }
+
mNative.displayRemoved(displayId);
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index e62c5c1..1703310 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -30,6 +30,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -148,10 +149,11 @@
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
- boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
+ boolean restarting, @InputMethodNavButtonFlags int navButtonFlags,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
try {
mTarget.startInput(startInputToken, inputContext, attribute, restarting,
- navButtonFlags);
+ navButtonFlags, imeDispatcher);
} catch (RemoteException e) {
logRemoteException(e);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6af00b3..c759c64 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -150,6 +150,7 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
@@ -616,6 +617,12 @@
IInputContext mCurInputContext;
/**
+ * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to
+ * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME.
+ */
+ ImeOnBackInvokedDispatcher mCurImeDispatcher;
+
+ /**
* The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
*/
@Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection;
@@ -2623,7 +2630,7 @@
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
- navButtonFlags);
+ navButtonFlags, mCurImeDispatcher);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2733,7 +2740,8 @@
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
@NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
@StartInputReason int startInputReason,
- int unverifiedTargetSdkVersion) {
+ int unverifiedTargetSdkVersion,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
// If no method is currently selected, do nothing.
final String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
@@ -2777,6 +2785,7 @@
mCurClient = cs;
mCurInputContext = inputContext;
mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
+ mCurImeDispatcher = imeDispatcher;
mCurVirtualDisplayToScreenMatrix =
getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
mDisplayIdToShowIme);
@@ -3780,10 +3789,12 @@
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion) {
+ int unverifiedTargetSdkVersion,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, attribute, inputContext,
- remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
+ imeDispatcher);
}
@NonNull
@@ -3792,7 +3803,8 @@
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion) {
+ int unverifiedTargetSdkVersion,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -3829,7 +3841,7 @@
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
attribute, inputContext, remoteAccessibilityInputConnection,
- unverifiedTargetSdkVersion, userId);
+ unverifiedTargetSdkVersion, userId, imeDispatcher);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3857,7 +3869,8 @@
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
IInputContext inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3868,7 +3881,8 @@
+ InputMethodDebug.startInputFlagsToString(startInputFlags)
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
- + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
+ + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+ + " imeDispatcher=" + imeDispatcher);
}
final ClientState cs = mClients.get(client.asBinder());
@@ -3952,7 +3966,7 @@
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ startInputReason, unverifiedTargetSdkVersion, imeDispatcher);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3993,7 +4007,8 @@
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
- attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+ attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
+ imeDispatcher);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -4033,7 +4048,8 @@
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ startInputReason, unverifiedTargetSdkVersion,
+ imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4065,7 +4081,8 @@
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ startInputReason, unverifiedTargetSdkVersion,
+ imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4085,7 +4102,8 @@
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ startInputReason, unverifiedTargetSdkVersion,
+ imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4115,7 +4133,8 @@
}
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
- startInputReason, unverifiedTargetSdkVersion);
+ startInputReason, unverifiedTargetSdkVersion,
+ imeDispatcher);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 92703ec..604e8f3 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -60,8 +60,8 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* This is the system implementation of a Session. Apps will interact with the
@@ -1159,6 +1159,9 @@
public void sendCommand(String packageName, int pid, int uid, String command, Bundle args,
ResultReceiver cb) {
try {
+ final String reason = TAG + ":" + command;
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onCommand(packageName, pid, uid, command, args, cb);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in sendCommand.", e);
@@ -1168,6 +1171,9 @@
public void sendCustomAction(String packageName, int pid, int uid, String action,
Bundle args) {
try {
+ final String reason = TAG + ":custom-" + action;
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onCustomAction(packageName, pid, uid, action, args);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in sendCustomAction.", e);
@@ -1176,6 +1182,9 @@
public void prepare(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":prepare";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPrepare(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in prepare.", e);
@@ -1185,6 +1194,9 @@
public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId,
Bundle extras) {
try {
+ final String reason = TAG + ":prepareFromMediaId";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
@@ -1194,6 +1206,9 @@
public void prepareFromSearch(String packageName, int pid, int uid, String query,
Bundle extras) {
try {
+ final String reason = TAG + ":prepareFromSearch";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in prepareFromSearch.", e);
@@ -1202,6 +1217,9 @@
public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
try {
+ final String reason = TAG + ":prepareFromUri";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in prepareFromUri.", e);
@@ -1210,6 +1228,9 @@
public void play(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":play";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPlay(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in play.", e);
@@ -1219,6 +1240,9 @@
public void playFromMediaId(String packageName, int pid, int uid, String mediaId,
Bundle extras) {
try {
+ final String reason = TAG + ":playFromMediaId";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in playFromMediaId.", e);
@@ -1228,6 +1252,9 @@
public void playFromSearch(String packageName, int pid, int uid, String query,
Bundle extras) {
try {
+ final String reason = TAG + ":playFromSearch";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in playFromSearch.", e);
@@ -1236,6 +1263,9 @@
public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
try {
+ final String reason = TAG + ":playFromUri";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in playFromUri.", e);
@@ -1244,6 +1274,9 @@
public void skipToTrack(String packageName, int pid, int uid, long id) {
try {
+ final String reason = TAG + ":skipToTrack";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onSkipToTrack(packageName, pid, uid, id);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in skipToTrack", e);
@@ -1252,6 +1285,9 @@
public void pause(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":pause";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPause(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in pause.", e);
@@ -1260,6 +1296,9 @@
public void stop(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":stop";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onStop(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in stop.", e);
@@ -1268,6 +1307,9 @@
public void next(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":next";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onNext(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in next.", e);
@@ -1276,6 +1318,9 @@
public void previous(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":previous";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onPrevious(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in previous.", e);
@@ -1284,6 +1329,9 @@
public void fastForward(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":fastForward";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onFastForward(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in fastForward.", e);
@@ -1292,6 +1340,9 @@
public void rewind(String packageName, int pid, int uid) {
try {
+ final String reason = TAG + ":rewind";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onRewind(packageName, pid, uid);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in rewind.", e);
@@ -1300,6 +1351,9 @@
public void seekTo(String packageName, int pid, int uid, long pos) {
try {
+ final String reason = TAG + ":seekTo";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onSeekTo(packageName, pid, uid, pos);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in seekTo.", e);
@@ -1308,6 +1362,9 @@
public void rate(String packageName, int pid, int uid, Rating rating) {
try {
+ final String reason = TAG + ":rate";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onRate(packageName, pid, uid, rating);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in rate.", e);
@@ -1316,6 +1373,9 @@
public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) {
try {
+ final String reason = TAG + ":setPlaybackSpeed";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
@@ -1325,6 +1385,9 @@
public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService,
int direction) {
try {
+ final String reason = TAG + ":adjustVolume";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
if (asSystemService) {
mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
Process.SYSTEM_UID, direction);
@@ -1338,6 +1401,9 @@
public void setVolumeTo(String packageName, int pid, int uid, int value) {
try {
+ final String reason = TAG + ":setVolumeTo";
+ mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+ pid, uid, packageName, reason);
mCb.onSetVolumeTo(packageName, pid, uid, value);
} catch (RemoteException e) {
Log.e(TAG, "Remote failure in setVolumeTo.", e);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 4df54b7..fa0c6c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -166,18 +166,19 @@
* An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
*/
@GuardedBy("mLock")
- final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
+ private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
* A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
* IDs.
*/
@GuardedBy("mLock")
- private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
+ private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
/**
* All the share targets from the package
*/
+ @GuardedBy("mLock")
private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
/**
@@ -231,7 +232,9 @@
}
public int getShortcutCount() {
- return mShortcuts.size();
+ synchronized (mLock) {
+ return mShortcuts.size();
+ }
}
@Override
@@ -272,7 +275,9 @@
@Nullable
public ShortcutInfo findShortcutById(@Nullable final String id) {
if (id == null) return null;
- return mShortcuts.get(id);
+ synchronized (mLock) {
+ return mShortcuts.get(id);
+ }
}
public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -347,11 +352,14 @@
* Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
*/
private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
- final ShortcutInfo shortcut = mShortcuts.remove(id);
- if (shortcut != null) {
- removeIcon(shortcut);
- shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
- | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+ final ShortcutInfo shortcut;
+ synchronized (mLock) {
+ shortcut = mShortcuts.remove(id);
+ if (shortcut != null) {
+ removeIcon(shortcut);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
+ | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+ }
}
return shortcut;
}
@@ -524,14 +532,16 @@
public List<ShortcutInfo> deleteAllDynamicShortcuts() {
final long now = mShortcutUser.mService.injectCurrentTimeMillis();
boolean changed = false;
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.isDynamic() && si.isVisibleToPublisher()) {
- changed = true;
+ synchronized (mLock) {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isDynamic() && si.isVisibleToPublisher()) {
+ changed = true;
- si.setTimestamp(now);
- si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
- si.setRank(0); // It may still be pinned, so clear the rank.
+ si.setTimestamp(now);
+ si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ si.setRank(0); // It may still be pinned, so clear the rank.
+ }
}
}
removeAllShortcutsAsync();
@@ -874,59 +884,63 @@
*/
public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
@NonNull IntentFilter filter) {
- final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
- for (int i = 0; i < mShareTargets.size(); i++) {
- final ShareTargetInfo target = mShareTargets.get(i);
- for (ShareTargetInfo.TargetData data : target.mTargetData) {
- if (filter.hasDataType(data.mMimeType)) {
- // Matched at least with one data type
- matchedTargets.add(target);
- break;
- }
- }
- }
-
- if (matchedTargets.isEmpty()) {
- return new ArrayList<>();
- }
-
- // Get the list of all dynamic shortcuts in this package.
- final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
- // included in the result
- findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
- ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
- mShortcutUser.mService.mContext.getPackageName(),
- 0, /*getPinnedByAnyLauncher=*/ false);
-
- final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
- for (int i = 0; i < shortcuts.size(); i++) {
- final Set<String> categories = shortcuts.get(i).getCategories();
- if (categories == null || categories.isEmpty()) {
- continue;
- }
- for (int j = 0; j < matchedTargets.size(); j++) {
- // Shortcut must have all of share target categories
- boolean hasAllCategories = true;
- final ShareTargetInfo target = matchedTargets.get(j);
- for (int q = 0; q < target.mCategories.length; q++) {
- if (!categories.contains(target.mCategories[q])) {
- hasAllCategories = false;
+ synchronized (mLock) {
+ final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
+ for (int i = 0; i < mShareTargets.size(); i++) {
+ final ShareTargetInfo target = mShareTargets.get(i);
+ for (ShareTargetInfo.TargetData data : target.mTargetData) {
+ if (filter.hasDataType(data.mMimeType)) {
+ // Matched at least with one data type
+ matchedTargets.add(target);
break;
}
}
- if (hasAllCategories) {
- result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
- new ComponentName(getPackageName(), target.mTargetClass)));
- break;
+ }
+
+ if (matchedTargets.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ // Get the list of all dynamic shortcuts in this package.
+ final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
+ // included in the result
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
+ mShortcutUser.mService.mContext.getPackageName(),
+ 0, /*getPinnedByAnyLauncher=*/ false);
+
+ final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
+ for (int i = 0; i < shortcuts.size(); i++) {
+ final Set<String> categories = shortcuts.get(i).getCategories();
+ if (categories == null || categories.isEmpty()) {
+ continue;
+ }
+ for (int j = 0; j < matchedTargets.size(); j++) {
+ // Shortcut must have all of share target categories
+ boolean hasAllCategories = true;
+ final ShareTargetInfo target = matchedTargets.get(j);
+ for (int q = 0; q < target.mCategories.length; q++) {
+ if (!categories.contains(target.mCategories[q])) {
+ hasAllCategories = false;
+ break;
+ }
+ }
+ if (hasAllCategories) {
+ result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
+ new ComponentName(getPackageName(), target.mTargetClass)));
+ break;
+ }
}
}
+ return result;
}
- return result;
}
public boolean hasShareTargets() {
- return !mShareTargets.isEmpty();
+ synchronized (mLock) {
+ return !mShareTargets.isEmpty();
+ }
}
/**
@@ -935,38 +949,40 @@
* the app's Xml resource.
*/
int getSharingShortcutCount() {
- if (mShareTargets.isEmpty()) {
- return 0;
- }
-
- // Get the list of all dynamic shortcuts in this package
- final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
-
- int sharingShortcutCount = 0;
- for (int i = 0; i < shortcuts.size(); i++) {
- final Set<String> categories = shortcuts.get(i).getCategories();
- if (categories == null || categories.isEmpty()) {
- continue;
+ synchronized (mLock) {
+ if (mShareTargets.isEmpty()) {
+ return 0;
}
- for (int j = 0; j < mShareTargets.size(); j++) {
- // A SharingShortcut must have all of share target categories
- boolean hasAllCategories = true;
- final ShareTargetInfo target = mShareTargets.get(j);
- for (int q = 0; q < target.mCategories.length; q++) {
- if (!categories.contains(target.mCategories[q])) {
- hasAllCategories = false;
+
+ // Get the list of all dynamic shortcuts in this package
+ final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ int sharingShortcutCount = 0;
+ for (int i = 0; i < shortcuts.size(); i++) {
+ final Set<String> categories = shortcuts.get(i).getCategories();
+ if (categories == null || categories.isEmpty()) {
+ continue;
+ }
+ for (int j = 0; j < mShareTargets.size(); j++) {
+ // A SharingShortcut must have all of share target categories
+ boolean hasAllCategories = true;
+ final ShareTargetInfo target = mShareTargets.get(j);
+ for (int q = 0; q < target.mCategories.length; q++) {
+ if (!categories.contains(target.mCategories[q])) {
+ hasAllCategories = false;
+ break;
+ }
+ }
+ if (hasAllCategories) {
+ sharingShortcutCount++;
break;
}
}
- if (hasAllCategories) {
- sharingShortcutCount++;
- break;
- }
}
+ return sharingShortcutCount;
}
- return sharingShortcutCount;
}
/**
@@ -1090,19 +1106,25 @@
// Now prepare to publish manifest shortcuts.
List<ShortcutInfo> newManifestShortcutList = null;
- try {
- newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
- getPackageName(), getPackageUserId(), mShareTargets);
- } catch (IOException|XmlPullParserException e) {
- Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+ final int shareTargetSize;
+ synchronized (mLock) {
+ try {
+ shareTargetSize = mShareTargets.size();
+ newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
+ getPackageName(), getPackageUserId(), mShareTargets);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+ }
}
final int manifestShortcutSize = newManifestShortcutList == null ? 0
: newManifestShortcutList.size();
if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG,
- String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
- getPackageName(), manifestShortcutSize, mShareTargets.size()));
+ String.format(
+ "Package %s has %d manifest shortcut(s), and %d share target(s)",
+ getPackageName(), manifestShortcutSize, shareTargetSize));
}
+
if (isNewApp && (manifestShortcutSize == 0)) {
// If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1701,37 +1723,38 @@
@Override
public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
- final int size = mShortcuts.size();
- final int shareTargetSize = mShareTargets.size();
+ synchronized (mLock) {
+ final int size = mShortcuts.size();
+ final int shareTargetSize = mShareTargets.size();
- if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
- return; // nothing to write.
- }
+ if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
+ return; // nothing to write.
+ }
- out.startTag(null, TAG_ROOT);
+ out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
- ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
- ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
- if (!forBackup) {
- synchronized (mLock) {
- ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+ ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
+ ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
+ ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+ if (!forBackup) {
+ ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate
? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
}
- }
- getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
+ getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
- for (int j = 0; j < size; j++) {
- saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
- }
-
- if (!forBackup) {
- for (int j = 0; j < shareTargetSize; j++) {
- mShareTargets.get(j).saveToXml(out);
+ for (int j = 0; j < size; j++) {
+ saveShortcut(
+ out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
}
- }
- out.endTag(null, TAG_ROOT);
+ if (!forBackup) {
+ for (int j = 0; j < shareTargetSize; j++) {
+ mShareTargets.get(j).saveToXml(out);
+ }
+ }
+
+ out.endTag(null, TAG_ROOT);
+ }
}
private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup,
@@ -1917,38 +1940,38 @@
synchronized (ret.mLock) {
ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
- }
- ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
- ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
+ ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
+ ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
- final String tag = parser.getName();
- if (depth == outerDepth + 1) {
- switch (tag) {
- case ShortcutPackageInfo.TAG_ROOT:
- ret.getPackageInfo().loadFromXml(parser, fromBackup);
-
- continue;
- case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName,
- shortcutUser.getUserId(), fromBackup);
- // Don't use addShortcut(), we don't need to save the icon.
- ret.mShortcuts.put(si.getId(), si);
- continue;
- case TAG_SHARE_TARGET:
- ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
- continue;
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
}
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case ShortcutPackageInfo.TAG_ROOT:
+ ret.getPackageInfo().loadFromXml(parser, fromBackup);
+
+ continue;
+ case TAG_SHORTCUT:
+ final ShortcutInfo si = parseShortcut(parser, packageName,
+ shortcutUser.getUserId(), fromBackup);
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mShortcuts.put(si.getId(), si);
+ continue;
+ case TAG_SHARE_TARGET:
+ ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
+ continue;
+ }
+ }
+ ShortcutService.warnForInvalidTag(depth, tag);
}
- ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
@@ -2152,7 +2175,9 @@
@VisibleForTesting
List<ShareTargetInfo> getAllShareTargetsForTest() {
- return new ArrayList<>(mShareTargets);
+ synchronized (mLock) {
+ return new ArrayList<>(mShareTargets);
+ }
}
@Override
@@ -2291,15 +2316,19 @@
private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
Objects.requireNonNull(shortcuts);
- for (ShortcutInfo si : shortcuts) {
- mShortcuts.put(si.getId(), si);
+ synchronized (mLock) {
+ for (ShortcutInfo si : shortcuts) {
+ mShortcuts.put(si.getId(), si);
+ }
}
}
@Nullable
List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
- return ids.stream().map(mShortcuts::get)
- .filter(Objects::nonNull).collect(Collectors.toList());
+ synchronized (mLock) {
+ return ids.stream().map(mShortcuts::get)
+ .filter(Objects::nonNull).collect(Collectors.toList());
+ }
}
private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2318,10 +2347,12 @@
private void forEachShortcutStopWhen(
@NonNull final Function<ShortcutInfo, Boolean> cb) {
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.valueAt(i);
- if (cb.apply(si)) {
- return;
+ synchronized (mLock) {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (cb.apply(si)) {
+ return;
+ }
}
}
}
@@ -2461,6 +2492,7 @@
})));
}
+ @GuardedBy("mLock")
@Override
void scheduleSaveToAppSearchLocked() {
final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b37f980..247117e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -124,8 +124,7 @@
LocalServices.getService(WindowManagerInternal.class);
IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
- window = wmService.windowForClientLocked(null, focusedWindowToken,
- false /* throwOnError */);
+ window = wmService.getFocusedWindowLocked();
if (window == null) {
EmbeddedWindowController.EmbeddedWindow embeddedWindow =
@@ -147,12 +146,6 @@
}
if (window == null) {
- window = wmService.getFocusedWindowLocked();
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
- "Focused window found using wmService.getFocusedWindowLocked()");
- }
-
- if (window == null) {
// We don't have any focused window, fallback ont the top currentTask of the focused
// display.
ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
@@ -194,7 +187,6 @@
if (backType == BackNavigationInfo.TYPE_CALLBACK
|| currentActivity == null
|| currentTask == null
- || currentTask.getDisplayContent().getImeContainer().isVisible()
|| currentActivity.isActivityTypeHome()) {
return infoBuilder
.setType(backType)
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2975a95..d0baa23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2922,7 +2922,7 @@
}
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
- if (mDisplayPolicy == null) {
+ if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
return null;
}
return DisplayCutout.fromResourcesRectApproximation(
@@ -2931,7 +2931,7 @@
}
RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
- if (mDisplayPolicy == null) {
+ if (mDisplayPolicy == null || mInitialRoundedCorners == null) {
return null;
}
return RoundedCorners.fromResources(
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 65062e5..ea72e12 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -573,7 +573,7 @@
recentsAnimationController.getTargetAppDisplayArea();
if (targetDA != null) {
mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
- mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+ mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
@@ -584,10 +584,14 @@
final Task rootTask = w.getTask().getRootTask();
mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop(
rootTask.getSurfaceControl());
+ final DisplayArea targetDA = rootTask.getDisplayArea();
// We set the layer to z=MAX-1 so that it's always on top.
- mPipInputConsumer.reparent(mInputTransaction, rootTask);
- mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
- mAddPipInputConsumerHandle = false;
+ if (targetDA != null) {
+ mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds());
+ mPipInputConsumer.reparent(mInputTransaction, targetDA);
+ mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+ mAddPipInputConsumerHandle = false;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 4068a97..c7f8a1e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -158,7 +158,7 @@
void continueStartingAnimations() {
synchronized (mLock) {
mAnimationStartDeferred = false;
- if (!mPendingAnimations.isEmpty()) {
+ if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
@@ -204,7 +204,7 @@
mPreProcessingAnimations.remove(animationLeash);
mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
+ if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
@@ -214,7 +214,7 @@
if (!requiresEdgeExtension) {
mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
+ if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
@@ -330,6 +330,14 @@
private void startAnimations(long frameTimeNanos) {
synchronized (mLock) {
+ if (!mPreProcessingAnimations.isEmpty()) {
+ // We only want to start running animations once all mPreProcessingAnimations have
+ // been processed to ensure preprocessed animations start in sync.
+ // NOTE: This means we might delay running animations that require preprocessing if
+ // new animations that also require preprocessing are requested before the previous
+ // ones have finished (see b/227449117).
+ return;
+ }
startPendingAnimationsLocked();
}
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
@@ -553,4 +561,4 @@
return mAnimationHandler;
}
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3906a19..a9f56d3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8877,16 +8877,18 @@
WindowState newFocusTarget = displayContent == null
? null : displayContent.findFocusedWindow();
if (newFocusTarget == null) {
- ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for "
- + "win=%s dropped since no candidate was found",
+ t.setFocusedWindow(null, null, displayId).apply();
+ ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s"
+ + " dropped focus so setting focus to null since no candidate"
+ + " was found",
embeddedWindow);
return;
}
- t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
- inputToken, embeddedWindow.toString(),
+ t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
displayId).apply();
+
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
- "Transfer focus request " + newFocusTarget,
+ "Focus request " + newFocusTarget,
"reason=grantEmbeddedWindowFocus(false)");
}
ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d2e56fa..ed9b2f0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -378,7 +378,7 @@
SurfaceControl.Builder makeSurface() {
final SurfaceControl.Builder builder = super.makeSurface();
if (mRoundedCornerOverlay) {
- builder.setParent(null);
+ builder.setParent(getDisplayContent().getSurfaceControl());
}
return builder;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8a7134e..7f466f3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8513,6 +8513,13 @@
}
}
+ private boolean isDeviceOwnerUserId(int userId) {
+ synchronized (getLockObject()) {
+ return mOwners.getDeviceOwnerComponent() != null
+ && mOwners.getDeviceOwnerUserId() == userId;
+ }
+ }
+
private boolean isDeviceOwnerPackage(String packageName, int userId) {
synchronized (getLockObject()) {
return mOwners.hasDeviceOwner()
@@ -12117,10 +12124,11 @@
return;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ Preconditions.checkCallAuthorization((isProfileOwner(caller)
+ && isManagedProfile(caller.getUserId()))
|| isDefaultDeviceOwner(caller),
- "Caller is not profile owner or device owner;"
- + " only profile owner or device owner may control the preferential"
+ "Caller is not managed profile owner or device owner;"
+ + " only managed profile owner or device owner may control the preferential"
+ " network service");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
@@ -12147,11 +12155,12 @@
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ Preconditions.checkCallAuthorization((isProfileOwner(caller)
+ && isManagedProfile(caller.getUserId()))
|| isDefaultDeviceOwner(caller),
- "Caller is not profile owner or device owner;"
- + " only profile owner or device owner may retrieve the preferential"
- + " network service configurations");
+ "Caller is not managed profile owner or device owner;"
+ + " only managed profile owner or device owner may retrieve the "
+ + "preferential network service configurations");
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
@@ -18266,7 +18275,7 @@
private void updateNetworkPreferenceForUser(int userId,
List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
- if (!isManagedProfile(userId)) {
+ if (!isManagedProfile(userId) && !isDeviceOwnerUserId(userId)) {
return;
}
List<ProfileNetworkPreference> preferences = new ArrayList<>();
diff --git a/services/proguard.flags b/services/proguard.flags
index 425da6c..bad02b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -33,6 +33,11 @@
public <init>(...);
}
+# Accessed from com.android.compos APEX
+-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog {
+ public static void write(...);
+}
+
# Binder interfaces
-keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
-keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index cb97c9b..e78f0c7 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -20,9 +20,11 @@
import android.content.ContextWrapper
import android.hardware.display.DisplayViewport
import android.hardware.input.InputManagerInternal
+import android.os.IInputConstants
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.Display
+import android.view.PointerIcon
import androidx.test.InstrumentationRegistry
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -31,6 +33,7 @@
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doAnswer
@@ -38,6 +41,7 @@
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.junit.MockitoJUnit
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@@ -219,4 +223,23 @@
secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
verify(native, times(2)).setPointerDisplayId(anyInt())
}
-}
\ No newline at end of file
+
+ @Test
+ fun onDisplayRemoved_resetAllAdditionalInputProperties() {
+ localService.setVirtualMousePointerDisplayId(10)
+ localService.setPointerIconVisible(false, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ localService.setPointerAcceleration(5f, 10)
+ verify(native).setPointerAcceleration(eq(5f))
+
+ service.onDisplayRemoved(10)
+ verify(native).displayRemoved(eq(10))
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+ verify(native).setPointerAcceleration(
+ eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+
+ localService.setVirtualMousePointerDisplayId(10)
+ verify(native).setPointerDisplayId(eq(10))
+ verifyNoMoreInteractions(native)
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index a227cd3..035249e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -70,6 +70,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -516,6 +517,7 @@
}
}
+ @Ignore("Causing breakages so ignoring to resolve, b/231667368")
@Test
public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception {
int uid = Binder.getCallingUid();
@@ -539,6 +541,7 @@
testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2());
}
+ @Ignore("Causing breakages so ignoring to resolve, b/231667368")
@Test
public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception {
int uid = Binder.getCallingUid();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 49cd343..873d9f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -306,6 +306,7 @@
if (focus) {
doReturn(window.getWindowInfo().token)
.when(mWindowManagerInternal).getFocusedWindowToken();
+ doReturn(window).when(mWm).getFocusedWindowLocked();
}
}
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index fa6de1a..ac1f376 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1673,4 +1673,9 @@
return UNKNOWN;
}
}
+
+ /** @hide */
+ public static boolean isFailCauseExisting(@DataFailureCause int failCause) {
+ return sFailCauseMap.containsKey(failCause);
+ }
}