Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 4a1c325..9f5255b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -49878,6 +49878,7 @@
public interface WindowManager extends android.view.ViewManager {
method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
@@ -51459,7 +51460,6 @@
method public int describeContents();
method public void dump(android.util.Printer, String);
method public android.content.ComponentName getComponent();
- method public int getConfigChanges();
method public String getId();
method public int getIsDefaultResourceId();
method public String getPackageName();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d951405..9fde791 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2734,10 +2734,6 @@
method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
}
- public final class InputMethodInfo implements android.os.Parcelable {
- ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
- }
-
public final class InputMethodManager {
method public int getDisplayId();
method public boolean hasActiveInputConnection(@Nullable android.view.View);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 233f737..a24f871 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -394,6 +394,20 @@
}
/**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ * @hide
+ */
+ public static boolean supportsNonResizableMultiWindow() {
+ try {
+ return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1bc7c..d310e8f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -56,6 +56,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -200,9 +201,12 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L;
+ private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled";
private static final int MAX_UNFORWARDED_OPS = 10;
+ private static Boolean sFullLog = null;
+
final Context mContext;
@UnsupportedAppUsage
@@ -6972,6 +6976,26 @@
AppOpsManager(Context context, IAppOpsService service) {
mContext = context;
mService = service;
+
+ if (mContext != null) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ if (pm != null && pm.checkPermission(Manifest.permission.READ_DEVICE_CONFIG,
+ mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ mContext.getMainExecutor(), properties -> {
+ if (properties.getKeyset().contains(FULL_LOG)) {
+ sFullLog = properties.getBoolean(FULL_LOG, false);
+ }
+ });
+ return;
+ }
+ } catch (Exception e) {
+ // This manager was made before DeviceConfig is ready, so it's a low-level
+ // system app. We likely don't care about its logs.
+ }
+ }
+ sFullLog = false;
}
/**
@@ -9110,10 +9134,20 @@
StringBuilder sb = new StringBuilder();
for (int i = firstInteresting; i <= lastInteresting; i++) {
+ if (sFullLog == null) {
+ try {
+ sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ FULL_LOG, false);
+ } catch (SecurityException e) {
+ // This should not happen, but it may, in rare cases
+ sFullLog = false;
+ }
+ }
+
if (i != firstInteresting) {
sb.append('\n');
}
- if (sb.length() + trace[i].toString().length() > 600) {
+ if (!sFullLog && sb.length() + trace[i].toString().length() > 600) {
break;
}
sb.append(trace[i]);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 542f754..3bfddf7 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -289,6 +289,13 @@
void setSplitScreenResizing(boolean resizing);
boolean supportsLocalVoiceInteraction();
+ /**
+ * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
+ * if the request orientation is not met, and will be shown in size-compat mode if the container
+ * size has changed.
+ */
+ boolean supportsNonResizableMultiWindow();
+
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9198eb7..5cfcd66 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -171,7 +171,7 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
- (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
+ (IInputMethodPrivilegedOperations) args.arg2);
} finally {
args.recycle();
}
@@ -280,10 +280,9 @@
@BinderThread
@Override
public void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privOps, int configChanges) {
+ IInputMethodPrivilegedOperations privOps) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
- configChanges));
+ mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 40a0fc4..7e2be01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -70,7 +70,6 @@
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -132,7 +131,6 @@
import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
@@ -515,8 +513,6 @@
private boolean mIsAutomotive;
private Handler mHandler;
private boolean mImeSurfaceScheduledForRemoval;
- private Configuration mLastKnownConfig;
- private int mHandledConfigChanges;
/**
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -592,13 +588,12 @@
@MainThread
@Override
public final void initializeInternal(@NonNull IBinder token, int displayId,
- IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+ IInputMethodPrivilegedOperations privilegedOperations) {
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
Log.w(TAG, "The token has already registered, ignore this initialization.");
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
- mHandledConfigChanges = configChanges;
mPrivOps.set(privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
updateInputMethodDisplay(displayId);
@@ -826,9 +821,6 @@
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
final boolean isVisible = isInputViewShown();
- if (isVisible && getResources() != null) {
- mLastKnownConfig = new Configuration(getResources().getConfiguration());
- }
final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
resultReceiver.send(visibilityChanged
@@ -1436,30 +1428,10 @@
* state: {@link #onStartInput} if input is active, and
* {@link #onCreateInputView} and {@link #onStartInputView} and related
* appropriate functions if the UI is displayed.
- * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration
- * changes themselves instead of being restarted with
- * {@link android.R.styleable#InputMethod_configChanges}.
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (shouldImeRestartForConfig(newConfig)) {
- resetStateForNewConfiguration();
- }
- }
-
- /**
- * @return {@code true} if {@link InputMethodService} needs to restart to handle
- * .{@link #onConfigurationChanged(Configuration)}
- */
- @VisibleForTesting
- boolean shouldImeRestartForConfig(@NonNull Configuration newConfig) {
- if (mLastKnownConfig == null) {
- return true;
- }
- // If the new config is the same as the config this Service is already running with,
- // then don't bother calling resetStateForNewConfiguration.
- int unhandledDiff = (mLastKnownConfig.diffPublicOnly(newConfig) & ~mHandledConfigChanges);
- return unhandledDiff != 0;
+ resetStateForNewConfiguration();
}
private void resetStateForNewConfiguration() {
@@ -3209,17 +3181,7 @@
requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
}
}
-
- @VisibleForTesting
- void setLastKnownConfig(@NonNull Configuration config) {
- mLastKnownConfig = config;
- }
-
- @VisibleForTesting
- void setHandledConfigChanges(int configChanges) {
- mHandledConfigChanges = configChanges;
- }
-
+
void startExtractingText(boolean inputChanged) {
final ExtractEditText eet = mExtractEditText;
if (eet != null && getCurrentInputStarted()
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 3880131..f61ab29 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,14 +16,26 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Formatter;
+import java.util.Locale;
+
/**
* @hide
*/
public final class Slog {
+ @GuardedBy("sMessageBuilder")
+ private static final StringBuilder sMessageBuilder = new StringBuilder();
+
+ @GuardedBy("sMessageBuilder")
+ private static final Formatter sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
+
private Slog() {
}
@@ -37,6 +49,15 @@
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.VERBOSE} message.
+ */
+ public static void v(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.VERBOSE)) return;
+
+ v(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int d(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -48,6 +69,15 @@
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.DEBUG} message.
+ */
+ public static void d(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.DEBUG)) return;
+
+ d(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -58,6 +88,15 @@
msg + '\n' + Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.INFO} message.
+ */
+ public static void i(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.INFO)) return;
+
+ i(tag, getMessage(format, args));
+ }
+
@UnsupportedAppUsage
public static int w(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -73,6 +112,24 @@
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
}
+ /**
+ * Logs a {@link Log.WARN} message.
+ */
+ public static void w(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.WARN} message with an exception
+ */
+ public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.WARN)) return;
+
+ w(tag, getMessage(format, args), exception);
+ }
+
@UnsupportedAppUsage
public static int e(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -85,6 +142,24 @@
}
/**
+ * Logs a {@link Log.ERROR} message.
+ */
+ public static void e(String tag, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@link Log.ERROR} message with an exception
+ */
+ public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
+ if (!Log.isLoggable(tag, Log.ERROR)) return;
+
+ e(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
* will always be handled asynchronously. Primarily for use by coding running within
* the system process.
@@ -95,6 +170,21 @@
}
/**
+ * Logs a {@code wtf} message.
+ */
+ public static void wtf(String tag, String format, @Nullable Object... args) {
+ wtf(tag, getMessage(format, args));
+ }
+
+ /**
+ * Logs a {@code wtf} message with an exception.
+ */
+ public static void wtf(String tag, Exception exception, String format,
+ @Nullable Object... args) {
+ wtf(tag, getMessage(format, args), exception);
+ }
+
+ /**
* Like {@link #wtf(String, String)}, but does not output anything to the log.
*/
public static void wtfQuiet(String tag, String msg) {
@@ -134,5 +224,13 @@
public static int println(int priority, String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
}
-}
+ private static String getMessage(String format, @Nullable Object... args) {
+ synchronized (sMessageBuilder) {
+ sFormatter.format(format, args);
+ String message = sMessageBuilder.toString();
+ sMessageBuilder.setLength(0);
+ return message;
+ }
+ }
+}
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index 5a1b850..55fc4f4 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -16,13 +16,19 @@
package android.view;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -42,7 +48,7 @@
private static final Object sLock = new Object();
private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal();
- private final ArraySet<Consumer<Boolean>> mListeners = new ArraySet();
+ private final ArrayMap<Consumer<Boolean>, Executor> mListeners = new ArrayMap();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private boolean mInternalListenerAttached = false;
private boolean mCrossWindowBlurEnabled;
@@ -74,20 +80,22 @@
}
}
- void addListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ void addListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(executor, "executor cannot be null");
synchronized (sLock) {
attachInternalListenerIfNeededLocked();
- mListeners.add(listener);
- notifyListenerOnMain(listener, mCrossWindowBlurEnabled);
+ mListeners.put(listener, executor);
+ notifyListener(listener, executor, mCrossWindowBlurEnabled);
}
}
void removeListener(Consumer<Boolean> listener) {
- if (listener == null) return;
+ Preconditions.checkNotNull(listener, "listener cannot be null");
synchronized (sLock) {
mListeners.remove(listener);
@@ -116,10 +124,8 @@
}
}
- private void notifyListenerOnMain(Consumer<Boolean> listener, boolean enabled) {
- mMainHandler.post(() -> {
- listener.accept(enabled);
- });
+ private void notifyListener(Consumer<Boolean> listener, Executor executor, boolean enabled) {
+ executor.execute(() -> listener.accept(enabled));
}
private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub {
@@ -128,8 +134,13 @@
synchronized (sLock) {
mCrossWindowBlurEnabled = enabled;
- for (int i = 0; i < mListeners.size(); i++) {
- notifyListenerOnMain(mListeners.valueAt(i), enabled);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mListeners.size(); i++) {
+ notifyListener(mListeners.keyAt(i), mListeners.valueAt(i), enabled);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 818a2b0..04512c9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -81,6 +81,7 @@
import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -121,6 +122,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -864,6 +866,33 @@
}
/**
+ * Adds a listener, which will be called when cross-window blurs are enabled/disabled at
+ * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius})
+ * and window background blur (see {@link Window#setBackgroundBlurRadius}).
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+ * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+ * change its theme to one that does not use blurs.
+ *
+ * If the listener is added successfully, it will be called immediately with the current
+ * cross-window blur enabled state.
+ *
+ * @param executor {@link Executor} to handle the listener callback
+ * @param listener the listener to be added. It will be called back with a boolean parameter,
+ * which is true if cross-window blur is enabled and false if it is disabled
+ *
+ * @see #removeCrossWindowBlurEnabledListener
+ * @see #isCrossWindowBlurEnabled
+ * @see LayoutParams#setBlurBehindRadius
+ * @see Window#setBackgroundBlurRadius
+ */
+ default void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ }
+
+ /**
* Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener}
*
* @param listener the listener to be removed
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index e37522b..8dce852 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -23,6 +23,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -40,6 +41,7 @@
import com.android.internal.os.IResultReceiver;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -310,7 +312,13 @@
@Override
public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
- CrossWindowBlurListeners.getInstance().addListener(listener);
+ addCrossWindowBlurEnabledListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> listener) {
+ CrossWindowBlurListeners.getInstance().addListener(executor, listener);
}
@Override
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6ade5e6..de4554b 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -105,7 +105,7 @@
*/
@MainThread
default void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+ IInputMethodPrivilegedOperations privilegedOperations) {
updateInputMethodDisplay(displayId);
attachToken(token);
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 25712f8..5d876a6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -18,23 +18,19 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
-import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -64,7 +60,6 @@
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
- * @attr ref android.R.styleable#InputMethod_configChanges
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -123,12 +118,6 @@
private final boolean mInlineSuggestionsEnabled;
/**
- * The flag for configurations IME assumes the responsibility for handling in
- * {@link InputMethodService#onConfigurationChanged(Configuration)}}.
- */
- private final int mHandledConfigChanges;
-
- /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -214,8 +203,6 @@
false);
inlineSuggestionsEnabled = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
- mHandledConfigChanges = sa.getInt(
- com.android.internal.R.styleable.InputMethod_configChanges, 0);
sa.recycle();
final int depth = parser.getDepth();
@@ -300,7 +287,6 @@
mIsVrOnly = source.readBoolean();
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
- mHandledConfigChanges = source.readInt();
mForceDefault = false;
}
@@ -312,22 +298,7 @@
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
- 0 /* handledConfigChanges */);
- }
-
- /**
- * Temporary API for creating a built-in input method for test.
- * @hide
- */
- @TestApi
- public InputMethodInfo(@NonNull String packageName, @NonNull String className,
- @NonNull CharSequence label, @NonNull String settingsActivity,
- int handledConfigChanges) {
- this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
- settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
- false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */);
}
/**
@@ -339,7 +310,7 @@
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
- false /* isVrOnly */, 0 /* handledconfigChanges */);
+ false /* isVrOnly */);
}
/**
@@ -350,8 +321,7 @@
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
- supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
- 0 /* handledConfigChanges */);
+ supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly);
}
/**
@@ -361,7 +331,7 @@
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
- boolean isVrOnly, int handledConfigChanges) {
+ boolean isVrOnly) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -373,7 +343,6 @@
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
mIsVrOnly = isVrOnly;
- mHandledConfigChanges = handledConfigChanges;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -520,17 +489,6 @@
}
}
- /**
- * Returns the bit mask of kinds of configuration changes that this IME
- * can handle itself (without being restarted by the system).
- *
- * @attr ref android.R.styleable#InputMethod_configChanges
- */
- @ActivityInfo.Config
- public int getConfigChanges() {
- return mHandledConfigChanges;
- }
-
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -621,7 +579,6 @@
dest.writeBoolean(mIsVrOnly);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
- dest.writeInt(mHandledConfigChanges);
}
/**
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33..c336373 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,8 +35,7 @@
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
- int configChanges);
+ void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f7b3f30..2e4be14 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1509,7 +1509,9 @@
res = JNI_TRUE;
} else {
jniThrowException(env, "java/util/NoSuchElementException",
- "Death link does not exist");
+ base::StringPrintf("Death link does not exist (%s)",
+ statusToString(err).c_str())
+ .c_str());
}
}
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 9746a07..97fdb43 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -34,6 +34,8 @@
#include "core_jni_helpers.h"
+using android::base::Result;
+
namespace android {
// Log debug messages about the dispatch cycle.
@@ -197,16 +199,9 @@
ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
- uint32_t publishedSeq;
- bool handled;
- std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
- [&publishedSeq, &handled](uint32_t inSeq, bool inHandled,
- nsecs_t inConsumeTime) -> void {
- publishedSeq = inSeq;
- handled = inHandled;
- };
- status_t status = mInputPublisher.receiveFinishedSignal(callback);
- if (status) {
+ Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
+ if (!result.ok()) {
+ const status_t status = result.error().code();
if (status == WOULD_BLOCK) {
return OK;
}
@@ -215,7 +210,7 @@
return status;
}
- auto it = mPublishedSeqMap.find(publishedSeq);
+ auto it = mPublishedSeqMap.find(result->seq);
if (it == mPublishedSeqMap.end()) {
continue;
}
@@ -225,9 +220,9 @@
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
- "pendingEvents=%zu.",
- getInputChannelName().c_str(), seq, handled ? "true" : "false",
- mPublishedSeqMap.size());
+ "pendingEvents=%zu.",
+ getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
+ mPublishedSeqMap.size());
}
if (!skipCallbacks) {
@@ -241,8 +236,8 @@
}
env->CallVoidMethod(senderObj.get(),
- gInputEventSenderClassInfo.dispatchInputEventFinished,
- jint(seq), jboolean(handled));
+ gInputEventSenderClassInfo.dispatchInputEventFinished,
+ static_cast<jint>(seq), static_cast<jboolean>(result->handled));
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching finished signal.");
skipCallbacks = true;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 99ad6d1..f9227617 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5419,6 +5419,8 @@
intents}.
<p>Protection level: normal -->
<permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:label="@string/permlab_fullScreenIntent"
+ android:description="@string/permdesc_fullScreenIntent"
android:protectionLevel="normal" />
<!-- @SystemApi Allows requesting the framework broadcast the
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5412db6..6ebd878 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3564,16 +3564,6 @@
<attr name="__removed2" format="boolean" />
<!-- Specifies whether the IME supports showing inline suggestions. -->
<attr name="supportsInlineSuggestions" format="boolean" />
- <!-- Specify one or more configuration changes that the IME will handle itself. If not
- specified, the IME will be restarted if any of these configuration changes happen in
- the system. Otherwise, the IME will remain running and its
- {@link android.inputmethodservice.InputMethodService#onConfigurationChanged}
- method is called with the new configuration.
- <p>Note that all of these configuration changes can impact the
- resource values seen by the application, so you will generally need
- to re-retrieve all resources (including view layouts, drawables, etc)
- to correctly handle any configuration change.-->
- <attr name="configChanges" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 387c065..930bb87 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -889,6 +889,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_expandStatusBar">Allows the app to expand or collapse the status bar.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fullScreenIntent">display notifications as full screen activities on a locked device</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fullScreenIntent">Allows the app to display notifications as full screen activities on a locked device</string>
+
<!-- Title of an application permission, listed so the user can install application shortcuts
in their Launcher -->
<string name="permlab_install_shortcut">install shortcuts</string>
@@ -5879,9 +5884,9 @@
<!-- Window magnification prompt related string. -->
<!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_title">New: Window Magnifier</string>
- <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
- <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+ <string name="window_magnification_prompt_title">Magnify part of your screen</string>
+ <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=NONE] -->
+ <string name="window_magnification_prompt_content">You can now magnify your full screen, a specific area, or switch between both options.</string>
<!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
<string name="turn_on_magnification_settings_action">Turn on in Settings</string>
<!-- Notification action to dismiss. [CHAR LIMIT=50] -->
diff --git a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java b/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
deleted file mode 100644
index 4863cfe..0000000
--- a/core/tests/coretests/src/android/inputmethodservice/InputMethodServiceTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-package android.inputmethodservice;
-
-import static android.content.res.Configuration.KEYBOARD_12KEY;
-import static android.content.res.Configuration.NAVIGATION_NONAV;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.rule.ServiceTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeoutException;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class InputMethodServiceTest {
- private InputMethodService mService;
- private Context mContext;
- @Rule
- public final ServiceTestRule serviceRule = new ServiceTestRule();
-
- @Before
- public void setUp() throws TimeoutException {
- mContext = getInstrumentation().getContext();
- mService = new InputMethodService();
- }
-
- @Test
- public void testShouldImeRestartForConfig() throws Exception {
- // Make sure we preserve Pre-S behavior i.e. Service restarts.
- mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
- Configuration config = mContext.getResources().getConfiguration();
- mService.setLastKnownConfig(config);
- assertTrue("IME should restart for Pre-S",
- mService.shouldImeRestartForConfig(config));
-
- // IME shouldn't restart on targetSdk S+ (with no config changes).
- mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
- assertFalse("IME shouldn't restart for S+",
- mService.shouldImeRestartForConfig(config));
-
- // Screen density changed but IME doesn't handle congfigChanges
- config.densityDpi = 99;
- assertTrue("IME should restart for unhandled configChanges",
- mService.shouldImeRestartForConfig(config));
-
- // opt-in IME to handle config changes.
- mService.setHandledConfigChanges(ActivityInfo.CONFIG_DENSITY);
- assertFalse("IME shouldn't restart for S+ since it handles configChanges",
- mService.shouldImeRestartForConfig(config));
- }
-}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 1b5dc8b..3f03302 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -38,6 +38,14 @@
path: "src",
}
+filegroup {
+ name: "wm_shell-aidls",
+ srcs: [
+ "src/**/*.aidl",
+ ],
+ path: "src",
+}
+
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
filegroup {
name: "wm_shell-sources-kt",
@@ -98,7 +106,7 @@
":wm_shell_protolog_src",
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
":wm_shell-sources-kt",
- "src/**/I*.aidl",
+ ":wm_shell-aidls",
],
resource_dirs: [
"res",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index eaed24d..d451f4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -47,21 +47,7 @@
private final ShellExecutor mMainExecutor;
private final HandlerImpl mImpl = new HandlerImpl();
- public static ShellCommandHandler create(
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHandedController> oneHandedOptional,
- Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<AppPairsController> appPairsOptional,
- ShellExecutor mainExecutor) {
- return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional,
- splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
- appPairsOptional, mainExecutor).mImpl;
- }
-
- private ShellCommandHandlerImpl(
+ public ShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@@ -80,6 +66,10 @@
mMainExecutor = mainExecutor;
}
+ public ShellCommandHandler asShellCommandHandler() {
+ return mImpl;
+ }
+
/** Dumps WM Shell internal state. */
private void dump(PrintWriter pw) {
mShellTaskOrganizer.dump(pw, "");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 85bd24c..6f4550c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -26,7 +26,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.startingsurface.StartingSurface;
+import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -47,44 +47,20 @@
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
- private final Optional<StartingSurface> mStartingSurfaceOptional;
+ private final StartingWindowController mStartingWindow;
private final InitImpl mImpl = new InitImpl();
- public static ShellInit create(DisplayImeController displayImeController,
+ public ShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
- Optional<StartingSurface> startingSurfaceOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
- ShellExecutor mainExecutor) {
- return new ShellInitImpl(displayImeController,
- dragAndDropController,
- shellTaskOrganizer,
- legacySplitScreenOptional,
- splitScreenOptional,
- appPairsOptional,
- startingSurfaceOptional,
- pipTouchHandlerOptional,
- fullscreenTaskListener,
- transitions,
- mainExecutor).mImpl;
- }
-
- private ShellInitImpl(DisplayImeController displayImeController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
- Optional<StartingSurface> startingSurfaceOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Transitions transitions,
+ StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
mDisplayImeController = displayImeController;
mDragAndDropController = dragAndDropController;
@@ -96,17 +72,21 @@
mPipTouchHandlerOptional = pipTouchHandlerOptional;
mTransitions = transitions;
mMainExecutor = mainExecutor;
- mStartingSurfaceOptional = startingSurfaceOptional;
+ mStartingWindow = startingWindow;
+ }
+
+ public ShellInit asShellInit() {
+ return mImpl;
}
private void init() {
// Start listening for display changes
mDisplayImeController.startMonitorDisplays();
+ // Setup the shell organizer
mShellTaskOrganizer.addListenerForType(
mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
- mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface);
- // Register the shell organizer
+ mShellTaskOrganizer.initStartingWindow(mStartingWindow);
mShellTaskOrganizer.registerOrganizer();
mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index fcb53cd..94d13ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -48,7 +48,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
-import com.android.wm.shell.startingsurface.StartingSurface;
+import com.android.wm.shell.startingsurface.StartingWindowController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -133,7 +133,7 @@
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
private final Object mLock = new Object();
- private StartingSurface mStartingSurface;
+ private StartingWindowController mStartingWindow;
/**
* In charge of showing size compat UI. Can be {@code null} if device doesn't support size
@@ -184,8 +184,8 @@
/**
* @hide
*/
- public void initStartingSurface(StartingSurface startingSurface) {
- mStartingSurface = startingSurface;
+ public void initStartingWindow(StartingWindowController startingWindow) {
+ mStartingWindow = startingWindow;
}
/**
@@ -302,23 +302,23 @@
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
- if (mStartingSurface != null) {
- mStartingSurface.addStartingWindow(info, appToken);
+ if (mStartingWindow != null) {
+ mStartingWindow.addStartingWindow(info, appToken);
}
}
@Override
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
- if (mStartingSurface != null) {
- mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
+ if (mStartingWindow != null) {
+ mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
}
}
@Override
public void copySplashScreenView(int taskId) {
- if (mStartingSurface != null) {
- mStartingSurface.copySplashScreenView(taskId);
+ if (mStartingWindow != null) {
+ mStartingWindow.copySplashScreenView(taskId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 562b32b..b6d408a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,6 +23,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
@@ -88,7 +89,8 @@
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
- if (!task1.isResizeable || !task2.isResizeable) {
+ if ((!task1.isResizeable || !task2.isResizeable)
+ && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
ProtoLog.e(WM_SHELL_TASK_ORG,
"Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
task1.isResizeable, task2.isResizeable);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java
new file mode 100644
index 0000000..b29058b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.common;
+
+import android.Manifest;
+import android.util.Slog;
+
+import java.util.function.Consumer;
+
+/**
+ * Helpers for working with executors
+ */
+public class ExecutorUtils {
+
+ /**
+ * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
+ * callback.
+ */
+ public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
+ String log, Consumer<T> callback) {
+ executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
+ false /* blocking */);
+ }
+
+ /**
+ * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
+ * callback.
+ */
+ public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
+ String log, Consumer<T> callback, boolean blocking) {
+ if (controllerInstance == null) return;
+
+ final RemoteCallable<T> controller = controllerInstance;
+ controllerInstance.getContext().enforceCallingPermission(
+ Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
+ if (blocking) {
+ try {
+ controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
+ callback.accept((T) controller);
+ });
+ } catch (InterruptedException e) {
+ Slog.e("ExecutorUtils", "Remote call failed", e);
+ }
+ } else {
+ controllerInstance.getRemoteCallExecutor().execute(() -> {
+ callback.accept((T) controller);
+ });
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
similarity index 61%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
index 54242be..30f535b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
@@ -14,12 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.common;
+
+import android.content.Context;
/**
- * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ * An interface for controllers that can receive remote calls.
*/
-oneway interface ISplitScreenListener {
- void onStagePositionChanged(int stage, int position);
- void onTaskStageChanged(int taskId, int stage, boolean visible);
-}
+public interface RemoteCallable<T> {
+ /**
+ * Returns a context used for permission checking.
+ */
+ Context getContext();
+
+ /**
+ * Returns the executor to post the handler callback to.
+ */
+ ShellExecutor getRemoteCallExecutor();
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index aab2334..9a09ca4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -235,7 +235,7 @@
mStarter.startShortcut(packageName, id, stage, position, opts, user);
} else {
mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
- mContext, null, stage, position, opts);
+ null, stage, position, opts);
}
}
@@ -295,7 +295,7 @@
@Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
@StagePosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
+ void startIntent(PendingIntent intent, Intent fillInIntent,
@StageType int stage, @StagePosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
@@ -337,9 +337,8 @@
}
@Override
- public void startIntent(PendingIntent intent, Context context,
- @Nullable Intent fillInIntent, int stage, int position,
- @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage,
+ int position, @Nullable Bundle options) {
try {
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
index eea5c08..d06064a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java
@@ -30,6 +30,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.IBinder;
@@ -91,9 +92,11 @@
// is nothing behind it.
((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
&& triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if a non-resizable is launched, we also need to leave split-screen.
+ // if a non-resizable is launched when it is not supported in multi window,
+ // we also need to leave split-screen.
|| ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.isResizeable);
+ && !triggerTask.isResizeable
+ && !ActivityTaskManager.supportsNonResizableMultiWindow());
// In both cases, dismiss the primary
if (shouldDismiss) {
WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 82468ad..5a2ef56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BooleanSupplier;
/**
* Proxy to simplify calls into window manager/activity manager
@@ -208,11 +209,17 @@
return false;
}
ActivityManager.RunningTaskInfo topHomeTask = null;
+ // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
+ // because the value can be changed at runtime.
+ final BooleanSupplier supportsNonResizableMultiWindow =
+ createSupportsNonResizableMultiWindowSupplier();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Only move resizeable task to split secondary. However, we have an exception
- // for non-resizable home because we will minimize to show it.
- if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
+ // Check whether to move resizeable task to split secondary.
+ // Also, we have an exception for non-resizable home because we will minimize to show
+ // it.
+ if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
+ && !supportsNonResizableMultiWindow.getAsBoolean()) {
continue;
}
// Only move fullscreen tasks to split secondary.
@@ -357,6 +364,21 @@
outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
}
+ /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
+ private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
+ return new BooleanSupplier() {
+ private Boolean mSupportsNonResizableMultiWindow;
+ @Override
+ public boolean getAsBoolean() {
+ if (mSupportsNonResizableMultiWindow == null) {
+ mSupportsNonResizableMultiWindow =
+ ActivityTaskManager.supportsNonResizableMultiWindow();
+ }
+ return mSupportsNonResizableMultiWindow;
+ }
+ };
+ }
+
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl
similarity index 66%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl
index 54242be..008b508 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl
@@ -14,12 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.onehanded;
/**
- * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ * Interface that is exposed to remote callers to manipulate the OneHanded feature.
*/
-oneway interface ISplitScreenListener {
- void onStagePositionChanged(int stage, int position);
- void onTaskStageChanged(int taskId, int stage, boolean visible);
+interface IOneHanded {
+
+ /**
+ * Enters one handed mode.
+ */
+ oneway void startOneHanded() = 1;
+
+ /**
+ * Exits one handed mode.
+ */
+ oneway void stopOneHanded() = 2;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 4f31c37..a7e9a01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -26,6 +26,14 @@
*/
@ExternalThread
public interface OneHanded {
+
+ /**
+ * Returns a binder that can be passed to an external process to manipulate OneHanded.
+ */
+ default IOneHanded createExternalInterface() {
+ return null;
+ }
+
/**
* Return one handed settings enabled or not.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 5fc7c98..8022c1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -18,6 +18,10 @@
import static android.os.UserHandle.USER_CURRENT;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+
+import android.Manifest;
+import android.annotation.BinderThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.om.IOverlayManager;
@@ -43,6 +47,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ExecutorUtils;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -54,7 +60,7 @@
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
-public class OneHandedController {
+public class OneHandedController implements RemoteCallable<OneHandedController> {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -239,6 +245,16 @@
return mImpl;
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
/**
* Set one handed enabled or disabled when user update settings
*/
@@ -567,8 +583,22 @@
}
}
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
@ExternalThread
private class OneHandedImpl implements OneHanded {
+ private IOneHandedImpl mIOneHanded;
+
+ @Override
+ public IOneHanded createExternalInterface() {
+ if (mIOneHanded != null) {
+ mIOneHanded.invalidate();
+ }
+ mIOneHanded = new IOneHandedImpl(OneHandedController.this);
+ return mIOneHanded;
+ }
+
@Override
public boolean isOneHandedEnabled() {
// This is volatile so return directly
@@ -637,4 +667,39 @@
});
}
}
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IOneHandedImpl extends IOneHanded.Stub {
+ private OneHandedController mController;
+
+ IOneHandedImpl(OneHandedController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
+ }
+
+ @Override
+ public void startOneHanded() {
+ executeRemoteCallWithTaskPermission(mController, "startOneHanded",
+ (controller) -> {
+ controller.startOneHanded();
+ });
+ }
+
+ @Override
+ public void stopOneHanded() {
+ executeRemoteCallWithTaskPermission(mController, "stopOneHanded",
+ (controller) -> {
+ controller.stopOneHanded();
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
new file mode 100644
index 0000000..a6ffa6e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+
+import com.android.wm.shell.pip.IPipAnimationListener;
+
+/**
+ * Interface that is exposed to remote callers to manipulate the Pip feature.
+ */
+interface IPip {
+
+ /**
+ * Notifies that Activity is about to be swiped to home with entering PiP transition and
+ * queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
+ *
+ * @param componentName ComponentName represents the Activity
+ * @param activityInfo ActivityInfo tied to the Activity
+ * @param pictureInPictureParams PictureInPictureParams tied to the Activity
+ * @param launcherRotation Launcher rotation to calculate the PiP destination bounds
+ * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
+ * @return destination bounds the PiP window should land into
+ */
+ Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
+ in PictureInPictureParams pictureInPictureParams,
+ int launcherRotation, int shelfHeight) = 1;
+
+ /**
+ * Notifies the swiping Activity to PiP onto home transition is finished
+ *
+ * @param componentName ComponentName represents the Activity
+ * @param destinationBounds the destination bounds the PiP window lands into
+ */
+ oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 2;
+
+ /**
+ * Sets listener to get pinned stack animation callbacks.
+ */
+ oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3;
+
+ /**
+ * Sets the shelf height and visibility.
+ */
+ oneway void setShelfHeight(boolean visible, int shelfHeight) = 4;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
similarity index 68%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
index 97aa512..2569b78 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.pip;
/**
- * Listener interface that Launcher attaches to SystemUI to get
- * pinned stack animation callbacks.
+ * Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks.
*/
-oneway interface IPinnedStackAnimationListener {
+oneway interface IPipAnimationListener {
/**
- * Notifies the pinned stack animation is started.
+ * Notifies the listener that the Pip animation is started.
*/
- void onPinnedStackAnimationStarted();
+ void onPipAnimationStarted();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index d14c3e3c..6d4773b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -16,15 +16,10 @@
package com.android.wm.shell.pip;
-import android.annotation.Nullable;
-import android.app.PictureInPictureParams;
-import android.content.ComponentName;
-import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -34,6 +29,14 @@
*/
@ExternalThread
public interface Pip {
+
+ /**
+ * Returns a binder that can be passed to an external process to manipulate PIP.
+ */
+ default IPip createExternalInterface() {
+ return null;
+ }
+
/**
* Expand PIP, it's possible that specific request to activate the window via Alt-tab.
*/
@@ -109,30 +112,6 @@
default void showPictureInPictureMenu() {}
/**
- * Called by Launcher when swiping an auto-pip enabled Activity to home starts
- * @param componentName {@link ComponentName} represents the Activity entering PiP
- * @param activityInfo {@link ActivityInfo} tied to the Activity
- * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity
- * @param launcherRotation Rotation Launcher is in
- * @param shelfHeight Shelf height when landing PiP window onto Launcher
- * @return Destination bounds of PiP window based on the parameters passed in
- */
- default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams,
- int launcherRotation, int shelfHeight) {
- return null;
- }
-
- /**
- * Called by Launcher when swiping an auto-pip enable Activity to home finishes
- * @param componentName {@link ComponentName} represents the Activity entering PiP
- * @param destinationBounds Destination bounds of PiP window
- */
- default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- return;
- }
-
- /**
* Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used
* for times where the PiP bounds could conflict with SystemUI elements, such as a stashed
* PiP and the Back-from-Edge gesture.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9a584c6..501b90e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -21,8 +21,10 @@
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
@@ -33,6 +35,7 @@
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -44,6 +47,7 @@
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
+import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -51,9 +55,13 @@
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ExecutorUtils;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.pip.IPip;
+import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -71,7 +79,8 @@
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
-public class PipController implements PipTransitionController.PipTransitionCallback {
+public class PipController implements PipTransitionController.PipTransitionCallback,
+ RemoteCallable<PipController> {
private static final String TAG = "PipController";
private Context mContext;
@@ -85,12 +94,12 @@
private PipBoundsState mPipBoundsState;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
- protected final PipImpl mImpl = new PipImpl();
+ protected final PipImpl mImpl;
private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation;
- private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
+ private IPipAnimationListener mPinnedStackAnimationRecentsCallback;
protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
@@ -264,6 +273,7 @@
}
mContext = context;
+ mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
@@ -366,6 +376,16 @@
});
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
private void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
@@ -474,7 +494,7 @@
mPipTaskOrganizer.setOneShotAnimationType(animationType);
}
- private void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
}
@@ -512,7 +532,11 @@
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
- mPinnedStackAnimationRecentsCallback.accept(true);
+ try {
+ mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e);
+ }
}
}
@@ -638,7 +662,21 @@
mPipInputConsumer.dump(pw, innerPrefix);
}
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
private class PipImpl implements Pip {
+ private IPipImpl mIPip;
+
+ @Override
+ public IPip createExternalInterface() {
+ if (mIPip != null) {
+ mIPip.invalidate();
+ }
+ mIPip = new IPipImpl(PipController.this);
+ return mIPip;
+ }
+
@Override
public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
mMainExecutor.execute(() -> {
@@ -696,13 +734,6 @@
}
@Override
- public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
- mMainExecutor.execute(() -> {
- PipController.this.setPinnedStackAnimationListener(callback);
- });
- }
-
- @Override
public void setPinnedStackAnimationType(int animationType) {
mMainExecutor.execute(() -> {
PipController.this.setPinnedStackAnimationType(animationType);
@@ -724,29 +755,6 @@
}
@Override
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams, int launcherRotation,
- int shelfHeight) {
- Rect[] result = new Rect[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, shelfHeight);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to start swipe pip to home");
- }
- return result[0];
- }
-
- @Override
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- mMainExecutor.execute(() -> {
- PipController.this.stopSwipePipToHome(componentName, destinationBounds);
- });
- }
-
- @Override
public void dump(PrintWriter pw) {
try {
mMainExecutor.executeBlocking(() -> {
@@ -757,4 +765,89 @@
}
}
}
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IPipImpl extends IPip.Stub {
+ private PipController mController;
+ private IPipAnimationListener mListener;
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ final PipController controller = mController;
+ controller.getRemoteCallExecutor().execute(() -> {
+ mListener = null;
+ controller.setPinnedStackAnimationListener(null);
+ });
+ }
+ };
+
+ IPipImpl(PipController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
+ }
+
+ @Override
+ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams, int launcherRotation,
+ int shelfHeight) {
+ Rect[] result = new Rect[1];
+ executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
+ (controller) -> {
+ result[0] = controller.startSwipePipToHome(componentName, activityInfo,
+ pictureInPictureParams, launcherRotation, shelfHeight);
+ }, true /* blocking */);
+ return result[0];
+ }
+
+ @Override
+ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
+ (controller) -> {
+ controller.stopSwipePipToHome(componentName, destinationBounds);
+ });
+ }
+
+ @Override
+ public void setShelfHeight(boolean visible, int height) {
+ executeRemoteCallWithTaskPermission(mController, "setShelfHeight",
+ (controller) -> {
+ controller.setShelfHeight(visible, height);
+ });
+ }
+
+ @Override
+ public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
+ executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener",
+ (controller) -> {
+ if (mListener != null) {
+ // Reset the old death recipient
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
+ if (listener != null) {
+ // Register the death recipient for the new listener to clear the listener
+ try {
+ listener.asBinder().linkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death");
+ return;
+ }
+ }
+ mListener = listener;
+ controller.setPinnedStackAnimationListener(listener);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
new file mode 100644
index 0000000..0c46eab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
+
+/**
+ * Interface that is exposed to remote callers to manipulate the splitscreen feature.
+ */
+interface ISplitScreen {
+
+ /**
+ * Registers a split screen listener.
+ */
+ oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1;
+
+ /**
+ * Unregisters a split screen listener.
+ */
+ oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2;
+
+ /**
+ * Hides the side-stage if it is currently visible.
+ */
+ oneway void setSideStageVisibility(boolean visible) = 3;
+
+ /**
+ * Removes a task from the side stage.
+ */
+ oneway void removeFromSideStage(int taskId) = 4;
+
+ /**
+ * Removes the split-screen stages.
+ */
+ oneway void exitSplitScreen() = 5;
+
+ /**
+ * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible.
+ */
+ oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6;
+
+ /**
+ * Starts a task in a stage.
+ */
+ oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;
+
+ /**
+ * Starts a shortcut in a stage.
+ */
+ oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
+ in Bundle options, in UserHandle user) = 8;
+
+ /**
+ * Starts an activity in a stage.
+ */
+ oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
+ int position, in Bundle options) = 9;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl
similarity index 83%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl
index 54242be..faab4c2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl
@@ -14,12 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.splitscreen;
/**
* Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
*/
oneway interface ISplitScreenListener {
+
+ /**
+ * Called when the stage position changes.
+ */
void onStagePositionChanged(int stage, int position);
+
+ /**
+ * Called when a task changes stages.
+ */
void onTaskStageChanged(int taskId, int stage, boolean visible);
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 25a84bd..340b55d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -35,7 +35,7 @@
* TODO: Figure out which of these are actually needed outside of the Shell
*/
@ExternalThread
-public interface SplitScreen extends DragAndDropPolicy.Starter {
+public interface SplitScreen {
/**
* Stage position isn't specified normally meaning to use what ever it is currently set to.
*/
@@ -89,35 +89,10 @@
void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
}
- /** @return {@code true} if split-screen is currently visible. */
- boolean isSplitScreenVisible();
- /** Moves a task in the side-stage of split-screen. */
- boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
- /** Moves a task in the side-stage of split-screen. */
- boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @StagePosition int sideStagePosition);
- /** Removes a task from the side-stage of split-screen. */
- boolean removeFromSideStage(int taskId);
- /** Sets the position of the side-stage. */
- void setSideStagePosition(@StagePosition int sideStagePosition);
- /** Hides the side-stage if it is currently visible. */
- void setSideStageVisibility(boolean visible);
-
- /** Removes the split-screen stages. */
- void exitSplitScreen();
- /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
- void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
- /** Gets the stage bounds. */
- void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
-
- void registerSplitScreenListener(SplitScreenListener listener);
- void unregisterSplitScreenListener(SplitScreenListener listener);
-
- void startTask(int taskId,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options);
- void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @StagePosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, Context context,
- @Nullable Intent fillInIntent, @StageType int stage,
- @StagePosition int position, @Nullable Bundle options);
+ /**
+ * Returns a binder that can be passed to an external process to manipulate SplitScreen.
+ */
+ default ISplitScreen createExternalInterface() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index bb6f6f2..d4362ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
@@ -34,19 +35,24 @@
import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
+import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
import java.io.PrintWriter;
@@ -55,7 +61,8 @@
* {@link SplitScreen}.
* @see StageCoordinator
*/
-public class SplitScreenController implements DragAndDropPolicy.Starter {
+public class SplitScreenController implements DragAndDropPolicy.Starter,
+ RemoteCallable<SplitScreenController> {
private static final String TAG = SplitScreenController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
@@ -84,6 +91,16 @@
return mImpl;
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
public void onOrganizerRegistered() {
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -172,13 +189,13 @@
}
}
- public void startIntent(PendingIntent intent, Context context,
- Intent fillInIntent, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, Intent fillInIntent,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
- intent.send(context, 0, fillInIntent, null, null, null, options);
+ intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -242,121 +259,170 @@
}
}
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
+ @ExternalThread
private class SplitScreenImpl implements SplitScreen {
+ private ISplitScreenImpl mISplitScreen;
+
@Override
- public boolean isSplitScreenVisible() {
- return mMainExecutor.executeBlockingForResult(() -> {
- return SplitScreenController.this.isSplitScreenVisible();
- }, Boolean.class);
+ public ISplitScreen createExternalInterface() {
+ if (mISplitScreen != null) {
+ mISplitScreen.invalidate();
+ }
+ mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
+ return mISplitScreen;
+ }
+ }
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class ISplitScreenImpl extends ISplitScreen.Stub {
+ private SplitScreenController mController;
+ private ISplitScreenListener mListener;
+ private final SplitScreen.SplitScreenListener mSplitScreenListener =
+ new SplitScreen.SplitScreenListener() {
+ @Override
+ public void onStagePositionChanged(int stage, int position) {
+ try {
+ if (mListener != null) {
+ mListener.onStagePositionChanged(stage, position);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onStagePositionChanged", e);
+ }
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ try {
+ if (mListener != null) {
+ mListener.onTaskStageChanged(taskId, stage, visible);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onTaskStageChanged", e);
+ }
+ }
+ };
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ final SplitScreenController controller = mController;
+ controller.getRemoteCallExecutor().execute(() -> {
+ mListener = null;
+ controller.unregisterSplitScreenListener(mSplitScreenListener);
+ });
+ }
+ };
+
+ public ISplitScreenImpl(SplitScreenController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
}
@Override
- public boolean moveToSideStage(int taskId, int sideStagePosition) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
- }, Boolean.class);
+ public void registerSplitScreenListener(ISplitScreenListener listener) {
+ executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
+ (controller) -> {
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
+ if (listener != null) {
+ try {
+ listener.asBinder().linkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death");
+ return;
+ }
+ }
+ mListener = listener;
+ controller.registerSplitScreenListener(mSplitScreenListener);
+ });
}
@Override
- public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- int sideStagePosition) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
- }, Boolean.class);
- }
-
- @Override
- public boolean removeFromSideStage(int taskId) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return SplitScreenController.this.removeFromSideStage(taskId);
- }, Boolean.class);
- }
-
- @Override
- public void setSideStagePosition(int sideStagePosition) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.setSideStagePosition(sideStagePosition);
- });
- }
-
- @Override
- public void setSideStageVisibility(boolean visible) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.setSideStageVisibility(visible);
- });
- }
-
- @Override
- public void enterSplitScreen(int taskId, boolean leftOrTop) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
- });
+ public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+ executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
+ (controller) -> {
+ if (mListener != null) {
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
+ mListener = null;
+ controller.unregisterSplitScreenListener(mSplitScreenListener);
+ });
}
@Override
public void exitSplitScreen() {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.exitSplitScreen();
- });
+ executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
+ (controller) -> {
+ controller.exitSplitScreen();
+ });
}
@Override
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
- });
+ executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide",
+ (controller) -> {
+ controller.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ });
}
@Override
- public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
- try {
- mMainExecutor.executeBlocking(() -> {
- SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
- outBottomOrRightBounds);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to get stage bounds in 2s");
- }
+ public void setSideStageVisibility(boolean visible) {
+ executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility",
+ (controller) -> {
+ controller.setSideStageVisibility(visible);
+ });
}
@Override
- public void registerSplitScreenListener(SplitScreenListener listener) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.registerSplitScreenListener(listener);
- });
- }
-
- @Override
- public void unregisterSplitScreenListener(SplitScreenListener listener) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.unregisterSplitScreenListener(listener);
- });
+ public void removeFromSideStage(int taskId) {
+ executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
+ (controller) -> {
+ controller.removeFromSideStage(taskId);
+ });
}
@Override
public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.startTask(taskId, stage, position, options);
- });
+ executeRemoteCallWithTaskPermission(mController, "startTask",
+ (controller) -> {
+ controller.startTask(taskId, stage, position, options);
+ });
}
@Override
public void startShortcut(String packageName, String shortcutId, int stage, int position,
@Nullable Bundle options, UserHandle user) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
- options, user);
- });
+ executeRemoteCallWithTaskPermission(mController, "startShortcut",
+ (controller) -> {
+ controller.startShortcut(packageName, shortcutId, stage, position,
+ options, user);
+ });
}
@Override
- public void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
- int stage, int position, @Nullable Bundle options) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.startIntent(intent, context, fillInIntent, stage,
- position, options);
- });
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+ @Nullable Bundle options) {
+ executeRemoteCallWithTaskPermission(mController, "startIntent",
+ (controller) -> {
+ controller.startIntent(intent, fillInIntent, stage, position, options);
+ });
}
}
-
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl
similarity index 62%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl
index 54242be..546c406 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.startingsurface;
+
+import com.android.wm.shell.startingsurface.IStartingWindowListener;
/**
- * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ * Interface that is exposed to remote callers to manipulate starting windows.
*/
-oneway interface ISplitScreenListener {
- void onStagePositionChanged(int stage, int position);
- void onTaskStageChanged(int taskId, int stage, boolean visible);
+interface IStartingWindow {
+ /**
+ * Sets listener to get task launching callbacks.
+ */
+ oneway void setStartingWindowListener(IStartingWindowListener listener) = 43;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl
similarity index 90%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl
index eb3e60c..f562c8f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents;
+package com.android.wm.shell.startingsurface;
/**
* Listener interface that Launcher attaches to SystemUI to get
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
index f258286..079d689 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -16,35 +16,15 @@
package com.android.wm.shell.startingsurface;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.window.StartingWindowInfo;
-
-import java.util.function.BiConsumer;
/**
* Interface to engage starting window feature.
*/
public interface StartingSurface {
- /**
- * Called when a task need a starting window.
- */
- void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken);
- /**
- * Called when the content of a task is ready to show, starting window can be removed.
- */
- void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation);
- /**
- * Called when the Task wants to copy the splash screen.
- * @param taskId
- */
- void copySplashScreenView(int taskId);
/**
- * Registers the starting window listener.
- *
- * @param listener The callback when need a starting window.
+ * Returns a binder that can be passed to an external process to manipulate starting windows.
*/
- void setStartingWindowListener(BiConsumer<Integer, Integer> listener);
+ default IStartingWindow createExternalInterface() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a694e52..b6ca869 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -25,6 +25,8 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
@@ -37,6 +39,9 @@
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -58,7 +63,7 @@
* constructor to keep everything synchronized.
* @hide
*/
-public class StartingWindowController {
+public class StartingWindowController implements RemoteCallable<StartingWindowController> {
private static final String TAG = StartingWindowController.class.getSimpleName();
static final boolean DEBUG_SPLASH_SCREEN = false;
static final boolean DEBUG_TASK_SNAPSHOT = false;
@@ -68,17 +73,17 @@
private BiConsumer<Integer, Integer> mTaskLaunchingCallback;
private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
+ private final Context mContext;
private final ShellExecutor mSplashScreenExecutor;
// For Car Launcher
public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
- mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor,
- new TransactionPool());
- mSplashScreenExecutor = splashScreenExecutor;
+ this(context, splashScreenExecutor, new TransactionPool());
}
public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
+ mContext = context;
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
mSplashScreenExecutor = splashScreenExecutor;
}
@@ -90,6 +95,16 @@
return mImpl;
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mSplashScreenExecutor;
+ }
+
private static class StartingTypeChecker {
TaskSnapshot mSnapshot;
@@ -188,59 +203,121 @@
/**
* Called when a task need a starting window.
*/
- void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
- final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
- final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
- if (mTaskLaunchingCallback != null) {
- mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
- }
- if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
- } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
- mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
- }
- // If prefer don't show, then don't show!
+ public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+ mSplashScreenExecutor.execute(() -> {
+ final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
+ final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
+ if (mTaskLaunchingCallback != null) {
+ mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
+ }
+ if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
+ } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
+ final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
+ }
+ // If prefer don't show, then don't show!
+ });
}
- void copySplashScreenView(int taskId) {
- mStartingSurfaceDrawer.copySplashScreenView(taskId);
+ public void copySplashScreenView(int taskId) {
+ mSplashScreenExecutor.execute(() -> {
+ mStartingSurfaceDrawer.copySplashScreenView(taskId);
+ });
}
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
- mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
+ mSplashScreenExecutor.execute(() -> {
+ mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
+ });
}
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
private class StartingSurfaceImpl implements StartingSurface {
+ private IStartingWindowImpl mIStartingWindow;
@Override
- public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
- mSplashScreenExecutor.execute(() ->
- StartingWindowController.this.addStartingWindow(windowInfo, appToken));
+ public IStartingWindowImpl createExternalInterface() {
+ if (mIStartingWindow != null) {
+ mIStartingWindow.invalidate();
+ }
+ mIStartingWindow = new IStartingWindowImpl(StartingWindowController.this);
+ return mIStartingWindow;
+ }
+ }
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IStartingWindowImpl extends IStartingWindow.Stub {
+ private StartingWindowController mController;
+ private IStartingWindowListener mListener;
+ private final BiConsumer<Integer, Integer> mStartingWindowListener =
+ this::notifyIStartingWindowListener;
+ private final IBinder.DeathRecipient mListenerDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ final StartingWindowController controller = mController;
+ controller.getRemoteCallExecutor().execute(() -> {
+ mListener = null;
+ controller.setStartingWindowListener(null);
+ });
+ }
+ };
+
+ public IStartingWindowImpl(StartingWindowController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
}
@Override
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
- mSplashScreenExecutor.execute(() ->
- StartingWindowController.this.removeStartingWindow(taskId, leash, frame,
- playRevealAnimation));
+ public void setStartingWindowListener(IStartingWindowListener listener) {
+ executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener",
+ (controller) -> {
+ if (mListener != null) {
+ // Reset the old death recipient
+ mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ }
+ if (listener != null) {
+ try {
+ listener.asBinder().linkToDeath(mListenerDeathRecipient,
+ 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death");
+ return;
+ }
+ }
+ mListener = listener;
+ controller.setStartingWindowListener(mStartingWindowListener);
+ });
}
- @Override
- public void copySplashScreenView(int taskId) {
- mSplashScreenExecutor.execute(() ->
- StartingWindowController.this.copySplashScreenView(taskId));
- }
+ private void notifyIStartingWindowListener(int taskId, int supportedType) {
+ if (mListener == null) {
+ return;
+ }
- @Override
- public void setStartingWindowListener(BiConsumer<Integer, Integer> listener) {
- mSplashScreenExecutor.execute(() ->
- StartingWindowController.this.setStartingWindowListener(listener));
+ try {
+ mListener.onTaskLaunching(taskId, supportedType);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify task launching", e);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
similarity index 63%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
copy to libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index 85bbf74..dffc700 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -16,25 +16,22 @@
package com.android.wm.shell.transition;
-import android.annotation.NonNull;
import android.window.IRemoteTransition;
import android.window.TransitionFilter;
-import com.android.wm.shell.common.annotations.ExternalThread;
-
/**
- * Interface to manage remote transitions.
+ * Interface that is exposed to remote callers to manipulate the transitions feature.
*/
-@ExternalThread
-public interface RemoteTransitions {
- /**
- * Registers a remote transition.
- */
- void registerRemote(@NonNull TransitionFilter filter,
- @NonNull IRemoteTransition remoteTransition);
+interface IShellTransitions {
/**
- * Unregisters a remote transition.
+ * Registers a remote transition handler.
*/
- void unregisterRemote(@NonNull IRemoteTransition remoteTransition);
+ oneway void registerRemote(in TransitionFilter filter,
+ in IRemoteTransition remoteTransition) = 1;
+
+ /**
+ * Unregisters a remote transition handler.
+ */
+ oneway void unregisterRemote(in IRemoteTransition remoteTransition) = 2;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index ac93a17..9667f4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.transition;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -23,6 +25,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
@@ -31,6 +34,8 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.BinderThread;
+
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -42,6 +47,8 @@
* if the request includes a specific remote.
*/
public class RemoteTransitionHandler implements Transitions.TransitionHandler {
+ private static final String TAG = "RemoteTransitionHandler";
+
private final ShellExecutor mMainExecutor;
/** Includes remotes explicitly requested by, eg, ActivityOptions */
@@ -51,15 +58,33 @@
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
new ArrayList<>();
+ private final IBinder.DeathRecipient mTransitionDeathRecipient =
+ new IBinder.DeathRecipient() {
+ @Override
+ @BinderThread
+ public void binderDied() {
+ mMainExecutor.execute(() -> {
+ mFilters.clear();
+ });
+ }
+ };
+
RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
}
void addFiltered(TransitionFilter filter, IRemoteTransition remote) {
+ try {
+ remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death");
+ return;
+ }
mFilters.add(new Pair<>(filter, remote));
}
void removeFiltered(IRemoteTransition remote) {
+ remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
for (int i = mFilters.size() - 1; i >= 0; --i) {
if (mFilters.get(i).second == remote) {
mFilters.remove(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
similarity index 83%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
index 85bbf74..bc42c6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java
@@ -26,7 +26,15 @@
* Interface to manage remote transitions.
*/
@ExternalThread
-public interface RemoteTransitions {
+public interface ShellTransitions {
+
+ /**
+ * Returns a binder that can be passed to an external process to manipulate remote transitions.
+ */
+ default IShellTransitions createExternalInterface() {
+ return null;
+ }
+
/**
* Registers a remote transition.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 677db10..ca1b53d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -23,6 +23,8 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -51,6 +53,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -60,7 +63,7 @@
import java.util.Arrays;
/** Plays transition animations */
-public class Transitions {
+public class Transitions implements RemoteCallable<Transitions> {
static final String TAG = "ShellTransitions";
/** Set to {@code true} to enable shell transitions. */
@@ -73,7 +76,7 @@
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
- private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
+ private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -87,10 +90,6 @@
/** Keeps track of currently tracked transitions and all the animations associated with each */
private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
- public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
- return transitions.mImpl;
- }
-
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
@@ -126,6 +125,20 @@
mRemoteTransitionHandler = null;
}
+ public ShellTransitions asRemoteTransitions() {
+ return mImpl;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
private void dispatchAnimScaleSetting(float scale) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
mHandlers.get(i).setAnimScaleSetting(scale);
@@ -134,8 +147,8 @@
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
- public static RemoteTransitions createEmptyForTesting() {
- return new RemoteTransitions() {
+ public static ShellTransitions createEmptyForTesting() {
+ return new ShellTransitions() {
@Override
public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
@androidx.annotation.NonNull IRemoteTransition remoteTransition) {
@@ -426,24 +439,74 @@
}
}
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
@ExternalThread
- private class RemoteTransitionImpl implements RemoteTransitions {
+ private class ShellTransitionImpl implements ShellTransitions {
+ private IShellTransitionsImpl mIShellTransitions;
+
+ @Override
+ public IShellTransitions createExternalInterface() {
+ if (mIShellTransitions != null) {
+ mIShellTransitions.invalidate();
+ }
+ mIShellTransitions = new IShellTransitionsImpl(Transitions.this);
+ return mIShellTransitions;
+ }
+
@Override
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull IRemoteTransition remoteTransition) {
mMainExecutor.execute(() -> {
- Transitions.this.registerRemote(filter, remoteTransition);
+ mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
});
}
@Override
public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
mMainExecutor.execute(() -> {
- Transitions.this.unregisterRemote(remoteTransition);
+ mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
}
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IShellTransitionsImpl extends IShellTransitions.Stub {
+ private Transitions mTransitions;
+
+ IShellTransitionsImpl(Transitions transitions) {
+ mTransitions = transitions;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mTransitions = null;
+ }
+
+ @Override
+ public void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull IRemoteTransition remoteTransition) {
+ executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
+ (transitions) -> {
+ transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
+ });
+ }
+
+ @Override
+ public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
+ executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
+ (transitions) -> {
+ transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
+ });
+ }
+ }
+
private class SettingsObserver extends ContentObserver {
SettingsObserver() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 63968f3..98ce274 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,7 @@
import android.os.SystemClock
import android.platform.test.annotations.Presubmit
+import android.provider.Settings
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -26,6 +27,8 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,11 +36,10 @@
import org.junit.runners.Parameterized
/**
- * Test AppPairs launch.
- * To run this test: `atest WMShellFlickerTests:AppPairsTest`
- */
-/**
- * Test cold launch app from launcher.
+ * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
+ * non-resizable apps.
+ *
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
@RequiresDevice
@@ -47,6 +49,7 @@
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -60,6 +63,24 @@
}
}
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 1) {
+ // Not support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
new file mode 100644
index 0000000..1e3595c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launch app from launcher. When the device supports non-resizable in multi window
+ * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
+ * non-resizable apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTestSupportPairNonResizeableApps(
+ testSpec: FlickerTestParameter
+) : AppPairsTransition(testSpec) {
+ var prevSupportNonResizableInMultiWindow = 0
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
+ transitions {
+ nonResizeableApp?.launchViaIntent(wmHelper)
+ // TODO pair apps through normal UX flow
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ }
+
+ @Before
+ fun setup() {
+ prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+ if (prevSupportNonResizableInMultiWindow == 0) {
+ // Support non-resizable in multi window
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1)
+ }
+ }
+
+ @After
+ fun teardown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ prevSupportNonResizableInMultiWindow)
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ @Presubmit
+ @Test
+ fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+
+ @Presubmit
+ @Test
+ fun bothAppWindowVisible() {
+ val nonResizeableApp = nonResizeableApp
+ require(nonResizeableApp != null) {
+ "Non resizeable app not initialized"
+ }
+ testSpec.assertWmEnd {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(primaryApp.defaultWindowName)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = AppPairsHelper.TEST_REPETITIONS)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 128560a..134d00b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.apppairs
import android.app.Instrumentation
+import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
import android.util.Log
@@ -46,6 +47,7 @@
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val context: Context = instrumentation.context
protected val isRotated = testSpec.config.startRotation.isRotated()
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index c1c4c6d..2f2bbba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -205,7 +205,7 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -217,12 +217,12 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -234,12 +234,12 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -251,7 +251,7 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -263,7 +263,7 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -276,13 +276,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -295,13 +295,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(), any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 92d9ca9..b0c5239 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -588,4 +588,7 @@
<!-- Determines whether the shell features all run on another thread. -->
<bool name="config_enableShellMainThread">false</bool>
+
+ <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
+ <bool name="allow_force_nav_bar_handle_opaque">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6207449..70be7c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2651,7 +2651,7 @@
<!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_description">Magnification switch</string>
<!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
- <string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
+ <string name="magnification_mode_switch_state_full_screen">Magnify full screen</string>
<!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_state_window">Magnify part of screen</string>
<!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 09e9675a..f98a959 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -41,6 +41,7 @@
srcs: [
"src/**/*.java",
"src/**/I*.aidl",
+ ":wm_shell-aidls",
],
static_libs: [
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 49e86f5..3da3085 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,11 +16,6 @@
package com.android.systemui.shared.recents;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -28,26 +23,15 @@
import android.os.UserHandle;
import android.view.MotionEvent;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.recents.ISplitScreenListener;
-import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
/**
* Temporary callbacks into SystemUI.
- * Next id = 44
*/
interface ISystemUiProxy {
/**
- * Proxies SurfaceControl.screenshotToBuffer().
- * @Removed
- * GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
- * int maxLayer, boolean useIdentityTransform, int rotation) = 0;
- */
-
- /**
* Begins screen pinning on the provided {@param taskId}.
*/
void startScreenPinning(int taskId) = 1;
@@ -63,15 +47,9 @@
Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
/**
- * Control the {@param alpha} of the back button in the navigation bar and {@param animate} if
- * needed from current value
- * @deprecated
- */
- void setBackButtonAlpha(float alpha, boolean animate) = 8;
-
- /**
* Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
- * and home bar in no-button mode) and {@param animate} if needed from current value
+ * and home handle & background in gestural mode). The {@param animate} is currently only
+ * supported for 2 button mode.
*/
void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
@@ -121,11 +99,6 @@
void stopScreenPinning() = 17;
/**
- * Sets the shelf height and visibility.
- */
- void setShelfHeight(boolean visible, int shelfHeight) = 20;
-
- /**
* Handle the provided image as if it was a screenshot.
*
* Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask
@@ -145,27 +118,12 @@
void notifySwipeToHomeFinished() = 23;
/**
- * Sets listener to get pinned stack animation callbacks.
- */
- void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24;
-
- /**
* Notifies that quickstep will switch to a new task
* @param rotation indicates which Surface.Rotation the gesture was started in
*/
void onQuickSwitchToNewTask(int rotation) = 25;
/**
- * Start the one-handed mode.
- */
- void startOneHandedMode() = 26;
-
- /**
- * Stop the one-handed mode.
- */
- void stopOneHandedMode() = 27;
-
- /**
* Handle the provided image as if it was a screenshot.
*/
void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen,
@@ -176,88 +134,5 @@
*/
void expandNotificationPanel() = 29;
- /**
- * Notifies that Activity is about to be swiped to home with entering PiP transition and
- * queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
- *
- * @param componentName ComponentName represents the Activity
- * @param activityInfo ActivityInfo tied to the Activity
- * @param pictureInPictureParams PictureInPictureParams tied to the Activity
- * @param launcherRotation Launcher rotation to calculate the PiP destination bounds
- * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
- * @return destination bounds the PiP window should land into
- */
- Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
- in PictureInPictureParams pictureInPictureParams,
- int launcherRotation, int shelfHeight) = 30;
-
- /**
- * Notifies the swiping Activity to PiP onto home transition is finished
- *
- * @param componentName ComponentName represents the Activity
- * @param destinationBounds the destination bounds the PiP window lands into
- */
- void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31;
-
- /**
- * Registers a RemoteTransitionCompat that will handle transitions. This parameter bundles an
- * IRemoteTransition and a filter that must pass for it.
- */
- void registerRemoteTransition(in RemoteTransitionCompat remoteTransition) = 32;
-
- /** Unegisters a RemoteTransitionCompat that will handle transitions. */
- void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33;
-
-// SplitScreen APIs...copied from SplitScreen.java
- /**
- * Stage position isn't specified normally meaning to use what ever it is currently set to.
- */
- //int STAGE_POSITION_UNDEFINED = -1;
- /**
- * Specifies that a stage is positioned at the top half of the screen if
- * in portrait mode or at the left half of the screen if in landscape mode.
- */
- //int STAGE_POSITION_TOP_OR_LEFT = 0;
- /**
- * Specifies that a stage is positioned at the bottom half of the screen if
- * in portrait mode or at the right half of the screen if in landscape mode.
- */
- //int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
-
- /**
- * Stage type isn't specified normally meaning to use what ever the default is.
- * E.g. exit split-screen and launch the app in fullscreen.
- */
- //int STAGE_TYPE_UNDEFINED = -1;
- /**
- * The main stage type.
- * @see MainStage
- */
- //int STAGE_TYPE_MAIN = 0;
- /**
- * The side stage type.
- * @see SideStage
- */
- //int STAGE_TYPE_SIDE = 1;
-
- void registerSplitScreenListener(in ISplitScreenListener listener) = 34;
- void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35;
-
- /** Hides the side-stage if it is currently visible. */
- void setSideStageVisibility(in boolean visible) = 36;
- /** Removes the split-screen stages. */
- void exitSplitScreen() = 37;
- /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
- void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
- void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
- void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
- in Bundle options, in UserHandle user) = 40;
- void startIntent(
- in PendingIntent intent, in Intent fillInIntent, in int stage, in int position,
- in Bundle options) = 41;
- void removeFromSideStage(in int taskId) = 42;
- /**
- * Sets listener to get task launching callbacks.
- */
- void setStartingWindowListener(IStartingWindowListener listener) = 43;
+ // Next id = 44
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 937c1df..41840af 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -41,6 +41,18 @@
public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor";
public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
+ // See IPip.aidl
+ public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
+ // See ISplitScreen.aidl
+ public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
+ // See IOneHanded.aidl
+ public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed";
+ // See IShellTransitions.aidl
+ public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS =
+ "extra_shell_shell_transitions";
+ // See IStartingWindow.aidl
+ public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
+ "extra_shell_starting_window";
public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 8f79de5..ed3d5ec 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -34,7 +34,7 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.transition.RemoteTransitions;
+import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
@@ -87,7 +87,7 @@
Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
@BindsInstance
- Builder setTransitions(RemoteTransitions t);
+ Builder setTransitions(ShellTransitions t);
@BindsInstance
Builder setStartingSurface(Optional<StartingSurface> s);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 1b77d1c..bbd95b4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -33,7 +33,7 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.transition.RemoteTransitions;
+import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
@@ -98,7 +98,7 @@
Optional<TaskViewFactory> getTaskViewFactory();
@WMSingleton
- RemoteTransitions getTransitions();
+ ShellTransitions getTransitions();
@WMSingleton
Optional<StartingSurface> getStartingSurface();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5536237..b471659 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -211,6 +211,7 @@
private Locale mLocale;
private int mLayoutDirection;
+ private boolean mAllowForceNavBarHandleOpaque;
private boolean mForceNavBarHandleOpaque;
private boolean mIsCurrentUserSetup;
@@ -333,13 +334,23 @@
// If the current user is not yet setup, then don't update any button alphas
return;
}
+ if (QuickStepContract.isLegacyMode(mNavBarMode)) {
+ // Don't allow the bar buttons to be affected by the alpha
+ return;
+ }
+
ButtonDispatcher buttonDispatcher = null;
boolean forceVisible = false;
- if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mNavigationBarView.getBackButton();
- } else if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- forceVisible = mForceNavBarHandleOpaque;
+ if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ // Disallow home handle animations when in gestural
+ animate = false;
+ forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
buttonDispatcher = mNavigationBarView.getHomeHandle();
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(alpha);
+ }
+ } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
+ buttonDispatcher = mNavigationBarView.getBackButton();
}
if (buttonDispatcher != null) {
buttonDispatcher.setVisibility(
@@ -506,6 +517,8 @@
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+ mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
+ R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
@@ -1468,6 +1481,12 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ if (!QuickStepContract.isGesturalMode(mode)) {
+ // Reset the override alpha
+ if (getBarTransitions() != null) {
+ getBarTransitions().setBackgroundOverrideAlpha(1f);
+ }
+ }
updateScreenPinningGestures();
if (!canShowSecondaryHandle()) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 61e1d61..fbc7c92 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -135,6 +136,10 @@
mBarBackground.setFrame(frame);
}
+ void setBackgroundOverrideAlpha(float alpha) {
+ mBarBackground.setOverrideAlpha(alpha);
+ }
+
@Override
protected boolean isLightsOut(int mode) {
return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -230,4 +235,17 @@
public void removeDarkIntensityListener(DarkIntensityListener listener) {
mDarkIntensityListeners.remove(listener);
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("NavigationBarTransitions:");
+ pw.println(" mMode: " + getMode());
+ pw.println(" mAlwaysOpaque: " + isAlwaysOpaque());
+ pw.println(" mAllowAutoDimWallpaperNotVisible: " + mAllowAutoDimWallpaperNotVisible);
+ pw.println(" mWallpaperVisible: " + mWallpaperVisible);
+ pw.println(" mLightsOut: " + mLightsOut);
+ pw.println(" mAutoDim: " + mAutoDim);
+ pw.println(" bg overrideAlpha: " + mBarBackground.getOverrideAlpha());
+ pw.println(" bg color: " + mBarBackground.getColor());
+ pw.println(" bg frame: " + mBarBackground.getFrame());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 148c665..091b17d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1158,6 +1158,8 @@
int frameHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_frame_height);
mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+ } else {
+ mBarTransitions.setBackgroundFrame(null);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -1331,6 +1333,7 @@
if (mNavigationInflaterView != null) {
mNavigationInflaterView.dump(pw);
}
+ mBarTransitions.dump(pw);
mContextualButtonGroup.dump(pw);
mRecentsOnboarding.dump(pw);
mRegionSamplingHelper.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a87bfd8..b0a3f43 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -25,6 +25,11 @@
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
@@ -35,15 +40,12 @@
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -57,14 +59,12 @@
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.Log;
import android.view.InputMonitor;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityManager;
-import android.window.IRemoteTransition;
import androidx.annotation.NonNull;
@@ -83,15 +83,11 @@
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.recents.ISplitScreenListener;
-import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -103,7 +99,7 @@
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.transition.RemoteTransitions;
+import com.android.wm.shell.transition.ShellTransitions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -111,7 +107,6 @@
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
-import java.util.function.Consumer;
import javax.inject.Inject;
@@ -151,12 +146,11 @@
private final ScreenshotHelper mScreenshotHelper;
private final Optional<OneHanded> mOneHandedOptional;
private final CommandQueue mCommandQueue;
- private final RemoteTransitions mShellTransitions;
+ private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
private Region mActiveNavBarRegion;
- private IPinnedStackAnimationListener mIPinnedStackAnimationListener;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private boolean mBound;
@@ -169,8 +163,6 @@
private float mWindowCornerRadius;
private boolean mSupportsRoundedCornersOnWindows;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
- private final ArraySet<IRemoteTransition> mRemoteTransitions = new ArraySet<>();
- private IStartingWindowListener mIStartingWindowListener;
@VisibleForTesting
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -294,11 +286,6 @@
}
@Override
- public void setBackButtonAlpha(float alpha, boolean animate) {
- setNavBarButtonAlpha(alpha, animate);
- }
-
- @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
if (!verifyCaller("onAssistantProgress")) {
return;
@@ -388,20 +375,6 @@
}
@Override
- public void setShelfHeight(boolean visible, int shelfHeight) {
- if (!verifyCaller("setShelfHeight")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mPipOptional.ifPresent(
- pip -> pip.setShelfHeight(visible, shelfHeight));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
Insets visibleInsets, int taskId) {
// Deprecated
@@ -429,36 +402,6 @@
}
@Override
- public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- if (!verifyCaller("setPinnedStackAnimationListener")) {
- return;
- }
- mIPinnedStackAnimationListener = listener;
- final long token = Binder.clearCallingIdentity();
- try {
- mPipOptional.ifPresent(
- pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void setStartingWindowListener(IStartingWindowListener listener) {
- if (!verifyCaller("setStartingWindowListener")) {
- return;
- }
- mIStartingWindowListener = listener;
- final long token = Binder.clearCallingIdentity();
- try {
- mStartingSurface.ifPresent(s ->
- s.setStartingWindowListener(mStartingWindowListener));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {
if (!verifyCaller("onQuickSwitchToNewTask")) {
return;
@@ -472,32 +415,6 @@
}
@Override
- public void startOneHandedMode() {
- if (!verifyCaller("startOneHandedMode")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void stopOneHandedMode() {
- if (!verifyCaller("stopOneHandedMode")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
Insets visibleInsets, Task.TaskKey task) {
mScreenshotHelper.provideScreenshot(
@@ -525,190 +442,6 @@
}
}
- @Override
- public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
- PictureInPictureParams pictureInPictureParams,
- int launcherRotation, int shelfHeight) {
- if (!verifyCaller("startSwipePipToHome")) {
- return null;
- }
- final long binderToken = Binder.clearCallingIdentity();
- try {
- return mPipOptional.map(pip ->
- pip.startSwipePipToHome(componentName, activityInfo,
- pictureInPictureParams, launcherRotation, shelfHeight))
- .orElse(null);
- } finally {
- Binder.restoreCallingIdentity(binderToken);
- }
- }
-
- @Override
- public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- if (!verifyCaller("stopSwipePipToHome")) {
- return;
- }
- final long binderToken = Binder.clearCallingIdentity();
- try {
- mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome(
- componentName, destinationBounds));
- } finally {
- Binder.restoreCallingIdentity(binderToken);
- }
- }
-
- @Override
- public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
- if (!verifyCaller("registerRemoteTransition")) return;
- final long binderToken = Binder.clearCallingIdentity();
- try {
- mRemoteTransitions.add(remoteTransition.getTransition());
- mShellTransitions.registerRemote(
- remoteTransition.getFilter(), remoteTransition.getTransition());
- } finally {
- Binder.restoreCallingIdentity(binderToken);
- }
- }
-
- @Override
- public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
- if (!verifyCaller("registerRemoteTransition")) return;
- final long binderToken = Binder.clearCallingIdentity();
- try {
- mRemoteTransitions.remove(remoteTransition.getTransition());
- mShellTransitions.unregisterRemote(remoteTransition.getTransition());
- } finally {
- Binder.restoreCallingIdentity(binderToken);
- }
- }
-
- @Override
- public void registerSplitScreenListener(ISplitScreenListener listener) {
- if (!verifyCaller("registerSplitScreenListener")) {
- return;
- }
- mISplitScreenListener = listener;
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(
- s -> s.registerSplitScreenListener(mSplitScreenListener));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void unregisterSplitScreenListener(ISplitScreenListener listener) {
- if (!verifyCaller("unregisterSplitScreenListener")) {
- return;
- }
- mISplitScreenListener = null;
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(
- s -> s.unregisterSplitScreenListener(mSplitScreenListener));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void setSideStageVisibility(boolean visible) {
- if (!verifyCaller("setSideStageVisibility")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void exitSplitScreen() {
- if (!verifyCaller("exitSplitScreen")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- if (!verifyCaller("exitSplitScreenOnHide")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void startTask(int taskId, int stage, int position, Bundle options) {
- if (!verifyCaller("startTask")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(
- s -> s.startTask(taskId, stage, position, options));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
- Bundle options, UserHandle user) {
- if (!verifyCaller("startShortcut")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(s ->
- s.startShortcut(packageName, shortcutId, stage, position, options, user));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- int stage, int position, Bundle options) {
- if (!verifyCaller("startIntent")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(s ->
- s.startIntent(intent, mContext, fillInIntent, stage, position, options));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
- public void removeFromSideStage(int taskId) {
- if (!verifyCaller("removeFromSideStage")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(
- s -> s.removeFromSideStage(taskId));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -762,6 +495,22 @@
params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
+
+ mPipOptional.ifPresent((pip) -> params.putBinder(
+ KEY_EXTRA_SHELL_PIP,
+ pip.createExternalInterface().asBinder()));
+ mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(
+ KEY_EXTRA_SHELL_SPLIT_SCREEN,
+ splitscreen.createExternalInterface().asBinder()));
+ mOneHandedOptional.ifPresent((onehanded) -> params.putBinder(
+ KEY_EXTRA_SHELL_ONE_HANDED,
+ onehanded.createExternalInterface().asBinder()));
+ params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
+ mShellTransitions.createExternalInterface().asBinder());
+ mStartingSurface.ifPresent((startingwindow) -> params.putBinder(
+ KEY_EXTRA_SHELL_STARTING_WINDOW,
+ startingwindow.createExternalInterface().asBinder()));
+
try {
mOverviewProxy.onInitialize(params);
} catch (RemoteException e) {
@@ -801,42 +550,11 @@
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
this::notifySplitScreenBoundsChanged;
- private final Consumer<Boolean> mPinnedStackAnimationCallback =
- this::notifyPinnedStackAnimationStarted;
-
- private final BiConsumer<Integer, Integer> mStartingWindowListener =
- this::notifyTaskLaunching;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
- private ISplitScreenListener mISplitScreenListener;
- private final SplitScreen.SplitScreenListener mSplitScreenListener =
- new SplitScreen.SplitScreenListener() {
- @Override
- public void onStagePositionChanged(int stage, int position) {
- try {
- if (mISplitScreenListener != null) {
- mISplitScreenListener.onStagePositionChanged(stage, position);
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "onStagePositionChanged", e);
- }
- }
-
- @Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- try {
- if (mISplitScreenListener != null) {
- mISplitScreenListener.onTaskStageChanged(taskId, stage, visible);
- }
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "onTaskStageChanged", e);
- }
- }
- };
-
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
@@ -849,7 +567,7 @@
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
- RemoteTransitions shellTransitions,
+ ShellTransitions shellTransitions,
Optional<StartingSurface> startingSurface) {
super(broadcastDispatcher);
mContext = context;
@@ -966,29 +684,6 @@
}
}
- private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) {
- if (mIPinnedStackAnimationListener == null) {
- return;
- }
- try {
- mIPinnedStackAnimationListener.onPinnedStackAnimationStarted();
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e);
- }
- }
-
- private void notifyTaskLaunching(int taskId, int supportedType) {
- if (mIStartingWindowListener == null) {
- return;
- }
-
- try {
- mIStartingWindowListener.onTaskLaunching(taskId, supportedType);
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call notifyTaskLaunching()", e);
- }
- }
-
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
@@ -1032,12 +727,6 @@
// Clean up the minimized state if launcher dies
mLegacySplitScreenOptional.ifPresent(
splitScreen -> splitScreen.setMinimized(false));
-
- // Clean up any registered remote transitions
- for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
- mShellTransitions.unregisterRemote(mRemoteTransitions.valueAt(i));
- }
- mRemoteTransitions.clear();
}
public void startConnectionToCurrentUser() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index e6731e6..c60bbc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -166,6 +166,7 @@
private int mGradientAlpha;
private int mColor;
+ private float mOverrideAlpha = 1f;
private PorterDuffColorFilter mTintFilter;
private Paint mPaint = new Paint();
@@ -195,6 +196,23 @@
mFrame = frame;
}
+ public void setOverrideAlpha(float overrideAlpha) {
+ mOverrideAlpha = overrideAlpha;
+ invalidateSelf();
+ }
+
+ public float getOverrideAlpha() {
+ return mOverrideAlpha;
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
@Override
public void setAlpha(int alpha) {
// noop
@@ -296,11 +314,13 @@
mGradient.setAlpha(mGradientAlpha);
mGradient.draw(canvas);
}
+
if (Color.alpha(mColor) > 0) {
mPaint.setColor(mColor);
if (mTintFilter != null) {
mPaint.setColorFilter(mTintFilter);
}
+ mPaint.setAlpha((int) (Color.alpha(mColor) * mOverrideAlpha));
if (mFrame != null) {
canvas.drawRect(mFrame, mPaint);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 1b6c612..1d18750 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -81,7 +81,7 @@
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
-import com.android.wm.shell.transition.RemoteTransitions;
+import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -399,8 +399,8 @@
@WMSingleton
@Provides
- static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
- return Transitions.asRemoteTransitions(transitions);
+ static ShellTransitions provideRemoteTransitions(Transitions transitions) {
+ return transitions.asRemoteTransitions();
}
@WMSingleton
@@ -509,27 +509,33 @@
@WMSingleton
@Provides
- static ShellInit provideShellInit(DisplayImeController displayImeController,
+ static ShellInit provideShellInit(ShellInitImpl impl) {
+ return impl.asShellInit();
+ }
+
+ @WMSingleton
+ @Provides
+ static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
- Optional<StartingSurface> startingSurface,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
+ StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
- return ShellInitImpl.create(displayImeController,
+ return new ShellInitImpl(displayImeController,
dragAndDropController,
shellTaskOrganizer,
legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
- startingSurface,
pipTouchHandlerOptional,
fullscreenTaskListener,
transitions,
+ startingWindow,
mainExecutor);
}
@@ -539,7 +545,13 @@
*/
@WMSingleton
@Provides
- static Optional<ShellCommandHandler> provideShellCommandHandler(
+ static Optional<ShellCommandHandler> provideShellCommandHandler(ShellCommandHandlerImpl impl) {
+ return Optional.of(impl.asShellCommandHandler());
+ }
+
+ @WMSingleton
+ @Provides
+ static ShellCommandHandlerImpl provideShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@@ -548,8 +560,8 @@
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
+ return new ShellCommandHandlerImpl(shellTaskOrganizer,
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, appPairsOptional, mainExecutor));
+ hideDisplayCutout, appPairsOptional, mainExecutor);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
deleted file mode 100644
index 25104b8..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents;
-
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.transition.RemoteTransitions;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-import dagger.Lazy;
-
-/**
- * Unit tests for {@link com.android.systemui.recents.OverviewProxyService}
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OverviewProxyServiceTest extends SysuiTestCase {
- private OverviewProxyService mSpiedOverviewProxyService;
- private TestableContext mSpiedContext;
-
- @Mock private BroadcastDispatcher mMockBroadcastDispatcher;
- @Mock private CommandQueue mMockCommandQueue;
- @Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy;
- @Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener;
- @Mock private NavigationModeController mMockNavModeController;
- @Mock private NotificationShadeWindowController mMockStatusBarWinController;
- @Mock private Optional<Pip> mMockPipOptional;
- @Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional;
- @Mock private Optional<SplitScreen> mMockSplitScreenOptional;
- @Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
- @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
- @Mock private PackageManager mPackageManager;
- @Mock private SysUiState mMockSysUiState;
- @Mock private RemoteTransitions mMockTransitions;
- @Mock private Optional<StartingSurface> mStartingSurface;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
- mSpiedContext = spy(mContext);
-
- when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
- when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-
- mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
- mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
- mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional,
- mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional,
- mMockBroadcastDispatcher, mMockTransitions, mStartingSurface));
- }
-
- @Test
- public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException {
- mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished();
-
- verify(mMockPipOptional, never()).ifPresent(any());
- }
-
- @Test
- public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException {
- mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener(
- mMockPinnedStackAnimationListener);
-
- verify(mMockPipOptional, never()).ifPresent(any());
- }
-
- @Test
- public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException {
- mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */,
- 100 /* shelfHeight */);
-
- verify(mMockPipOptional, never()).ifPresent(any());
- }
-}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 68c4a73..673749c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1841,7 +1841,7 @@
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
}
if (alreadyStartedOp) {
@@ -1863,7 +1863,7 @@
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ SystemClock.uptimeMillis());
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3765,6 +3765,7 @@
}
}
+ final long now = SystemClock.uptimeMillis();
// Check to see if the service had been started as foreground, but being
// brought down before actually showing a notification. That is not allowed.
if (r.fgRequired) {
@@ -3774,8 +3775,7 @@
r.fgWaiting = false;
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -3834,8 +3834,7 @@
decActiveForegroundAppLocked(smap, r);
ServiceState stracker = r.getTracker();
if (stracker != null) {
- stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
- r.lastActivity);
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3902,7 +3901,6 @@
}
int memFactor = mAm.mProcessStats.getMemFactorLocked();
- long now = SystemClock.uptimeMillis();
if (r.tracker != null) {
r.tracker.setStarted(false, memFactor, now);
r.tracker.setBound(false, memFactor, now);
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index ed62abc..2b0157c 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -49,8 +49,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
-import libcore.util.EmptyArray;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -85,6 +83,8 @@
private static final String TAG = DiscreteRegistry.class.getSimpleName();
private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
+ private static final long TIMELINE_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
private static final int CURRENT_VERSION = 1;
@@ -107,6 +107,8 @@
private static final String ATTR_UID_STATE = "us";
private static final String ATTR_FLAGS = "f";
+ private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -119,6 +121,9 @@
@GuardedBy("mInMemoryLock")
private DiscreteOps mDiscreteOps;
+ @GuardedBy("mOnDiskLock")
+ private DiscreteOps mCachedOps = null;
+
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
@@ -173,23 +178,25 @@
}
}
}
- }
- DiscreteOps discreteOps;
- synchronized (mInMemoryLock) {
- discreteOps = mDiscreteOps;
- mDiscreteOps = new DiscreteOps();
- }
- if (discreteOps.isEmpty()) {
- return;
- }
- long currentTimeStamp = Instant.now().toEpochMilli();
- try {
- final File file = new File(mDiscreteAccessDir, currentTimeStamp + TIMELINE_FILE_SUFFIX);
- discreteOps.writeToFile(file);
- } catch (Throwable t) {
- Slog.e(TAG,
- "Error writing timeline state: " + t.getMessage() + " "
- + Arrays.toString(t.getStackTrace()));
+ DiscreteOps discreteOps;
+ synchronized (mInMemoryLock) {
+ discreteOps = mDiscreteOps;
+ mDiscreteOps = new DiscreteOps();
+ mCachedOps = null;
+ }
+ if (discreteOps.isEmpty()) {
+ return;
+ }
+ long currentTimeStamp = Instant.now().toEpochMilli();
+ try {
+ final File file = new File(mDiscreteAccessDir,
+ currentTimeStamp + TIMELINE_FILE_SUFFIX);
+ discreteOps.writeToFile(file);
+ } catch (Throwable t) {
+ Slog.e(TAG,
+ "Error writing timeline state: " + t.getMessage() + " "
+ + Arrays.toString(t.getStackTrace()));
+ }
}
}
@@ -197,25 +204,33 @@
long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
@Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
- writeAndClearAccessHistory();
- DiscreteOps discreteOps = new DiscreteOps();
- readDiscreteOpsFromDisk(discreteOps, beginTimeMillis, endTimeMillis, filter, uidFilter,
- packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ DiscreteOps discreteOps = getAndCacheDiscreteOps();
+ discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, flagsFilter);
discreteOps.applyToHistoricalOps(result);
return;
}
- private void readDiscreteOpsFromDisk(DiscreteOps discreteOps, long beginTimeMillis,
- long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
- @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ private DiscreteOps getAndCacheDiscreteOps() {
+ DiscreteOps discreteOps = new DiscreteOps();
+
synchronized (mOnDiskLock) {
- long historyBeginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
- ChronoUnit.MILLIS).toEpochMilli();
- if (historyBeginTimeMillis > endTimeMillis) {
- return;
+ synchronized (mInMemoryLock) {
+ discreteOps.merge(mDiscreteOps);
}
- beginTimeMillis = max(beginTimeMillis, historyBeginTimeMillis);
+ if (mCachedOps == null) {
+ mCachedOps = new DiscreteOps();
+ readDiscreteOpsFromDisk(mCachedOps);
+ }
+ discreteOps.merge(mCachedOps);
+ }
+ return discreteOps;
+ }
+
+ private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
+ synchronized (mOnDiskLock) {
+ long beginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ ChronoUnit.MILLIS).toEpochMilli();
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
@@ -229,8 +244,7 @@
if (timestamp < beginTimeMillis) {
continue;
}
- discreteOps.readFromFile(f, beginTimeMillis, endTimeMillis, filter, uidFilter,
- packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
+ discreteOps.readFromFile(f, beginTimeMillis);
}
}
}
@@ -251,15 +265,11 @@
@AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
@NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
int nDiscreteOps) {
- DiscreteOps discreteOps = new DiscreteOps();
- synchronized (mOnDiskLock) {
- writeAndClearAccessHistory();
- String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING
- : new String[]{AppOpsManager.opToPublicName(dumpOp)};
- readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter,
- uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
- OP_FLAGS_ALL);
- }
+ DiscreteOps discreteOps = getAndCacheDiscreteOps();
+ String[] opNamesFilter = dumpOp == OP_NONE ? null
+ : new String[]{AppOpsManager.opToPublicName(dumpOp)};
+ discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
}
@@ -270,7 +280,7 @@
if (!isDiscreteUid(uid)) {
return false;
}
- if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) == 0) {
+ if ((flags & (OP_FLAGS_DISCRETE)) == 0) {
return false;
}
return true;
@@ -298,6 +308,19 @@
mUids = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mUids.isEmpty();
+ }
+
+ void merge(DiscreteOps other) {
+ int nUids = other.mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ int uid = other.mUids.keyAt(i);
+ DiscreteUidOps uidOps = other.mUids.valueAt(i);
+ getOrCreateDiscreteUidOps(uid).merge(uidOps);
+ }
+ }
+
void addDiscreteAccess(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
@@ -305,6 +328,25 @@
uidState, accessTime, accessDuration);
}
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_UID) != 0) {
+ ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
+ uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
+ mUids = uids;
+ }
+ int nUids = mUids.size();
+ for (int i = nUids - 1; i >= 0; i--) {
+ mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
+ opNamesFilter, attributionTagFilter, flagsFilter);
+ if (mUids.valueAt(i).isEmpty()) {
+ mUids.removeAt(i);
+ }
+ }
+ }
+
private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
@@ -353,14 +395,7 @@
return result;
}
- boolean isEmpty() {
- return mUids.isEmpty();
- }
-
- private void readFromFile(File f, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ private void readFromFile(File f, long beginTimeMillis) {
try {
FileInputStream stream = new FileInputStream(f);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -377,12 +412,7 @@
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
int uid = parser.getAttributeInt(null, ATTR_UID, -1);
- if ((filter & FILTER_BY_UID) != 0 && uid != uidFilter) {
- continue;
- }
- getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis,
- endTimeMillis, filter, packageNameFilter, opNamesFilter,
- attributionTagFilter, flagsFilter);
+ getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis);
}
}
} catch (Throwable t) {
@@ -400,6 +430,38 @@
mPackages = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mPackages.isEmpty();
+ }
+
+ void merge(DiscreteUidOps other) {
+ int nPackages = other.mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ String packageName = other.mPackages.keyAt(i);
+ DiscretePackageOps p = other.mPackages.valueAt(i);
+ getOrCreateDiscretePackageOps(packageName).merge(p);
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+ ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
+ packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
+ mPackages = packages;
+ }
+ int nPackages = mPackages.size();
+ for (int i = nPackages - 1; i >= 0; i--) {
+ mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
+ attributionTagFilter, flagsFilter);
+ if (mPackages.valueAt(i).isEmpty()) {
+ mPackages.removeAt(i);
+ }
+ }
+ }
+
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
@@ -445,22 +507,12 @@
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
- long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String packageNameFilter,
- @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- if ((filter & FILTER_BY_PACKAGE_NAME) != 0
- && !packageName.equals(packageNameFilter)) {
- continue;
- }
- getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis,
- endTimeMillis, filter, opNamesFilter, attributionTagFilter,
- flagsFilter);
+ getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis);
}
}
}
@@ -473,6 +525,10 @@
mPackageOps = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mPackageOps.isEmpty();
+ }
+
void addDiscreteAccess(int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
@@ -480,6 +536,35 @@
accessDuration);
}
+ void merge(DiscretePackageOps other) {
+ int nOps = other.mPackageOps.size();
+ for (int i = 0; i < nOps; i++) {
+ int opId = other.mPackageOps.keyAt(i);
+ DiscreteOp op = other.mPackageOps.valueAt(i);
+ getOrCreateDiscreteOp(opId).merge(op);
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
+ @AppOpsManager.OpFlags int flagsFilter) {
+ int nOps = mPackageOps.size();
+ for (int i = nOps - 1; i >= 0; i--) {
+ int opId = mPackageOps.keyAt(i);
+ if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
+ AppOpsManager.opToPublicName(opId))) {
+ mPackageOps.removeAt(i);
+ continue;
+ }
+ mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
+ attributionTagFilter, flagsFilter);
+ if (mPackageOps.valueAt(i).isEmpty()) {
+ mPackageOps.removeAt(i);
+ }
+ }
+ }
+
private DiscreteOp getOrCreateDiscreteOp(int op) {
DiscreteOp result = mPackageOps.get(op);
if (result == null) {
@@ -519,20 +604,12 @@
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_OP.equals(parser.getName())) {
int op = parser.getAttributeInt(null, ATTR_OP_ID);
- if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
- AppOpsManager.opToPublicName(op))) {
- continue;
- }
- getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis, endTimeMillis,
- filter, attributionTagFilter, flagsFilter);
+ getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis);
}
}
}
@@ -545,31 +622,66 @@
mAttributedOps = new ArrayMap<>();
}
+ boolean isEmpty() {
+ return mAttributedOps.isEmpty();
+ }
+
+ void merge(DiscreteOp other) {
+ int nTags = other.mAttributedOps.size();
+ for (int i = 0; i < nTags; i++) {
+ String tag = other.mAttributedOps.keyAt(i);
+ List<DiscreteOpEvent> otherEvents = other.mAttributedOps.valueAt(i);
+ List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(tag);
+ mAttributedOps.put(tag, stableListMerge(events, otherEvents));
+ }
+ }
+
+ private void filter(long beginTimeMillis, long endTimeMillis,
+ @AppOpsManager.HistoricalOpsRequestFilter int filter,
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
+ ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
+ attributedOps.put(attributionTagFilter,
+ getOrCreateDiscreteOpEventsList(attributionTagFilter));
+ mAttributedOps = attributedOps;
+ }
+
+ int nTags = mAttributedOps.size();
+ for (int i = nTags - 1; i >= 0; i--) {
+ String tag = mAttributedOps.keyAt(i);
+ List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
+ list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+ mAttributedOps.put(tag, list);
+ if (list.size() == 0) {
+ mAttributedOps.removeAt(i);
+ }
+ }
+ }
+
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
- accessTime = Instant.ofEpochMilli(accessTime).truncatedTo(
- ChronoUnit.MINUTES).toEpochMilli();
+ accessTime = accessTime / TIMELINE_QUANTIZATION * TIMELINE_QUANTIZATION;
int nAttributedOps = attributedOps.size();
- for (int i = nAttributedOps - 1; i >= 0; i--) {
- DiscreteOpEvent previousOp = attributedOps.get(i);
- if (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime
- && accessDuration > -1) {
- // existing event with updated duration
- attributedOps.remove(i);
- break;
- }
+ int i = nAttributedOps;
+ for (; i > 0; i--) {
+ DiscreteOpEvent previousOp = attributedOps.get(i - 1);
if (previousOp.mNoteTime < accessTime) {
break;
}
if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- return;
+ if (accessDuration != previousOp.mNoteDuration
+ && accessDuration > TIMELINE_QUANTIZATION) {
+ break;
+ } else {
+ return;
+ }
}
}
- attributedOps.add(new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
}
private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -633,18 +745,11 @@
}
}
- void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
- @AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) throws Exception {
+ void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_TAG.equals(parser.getName())) {
String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
- if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !attributionTag.equals(
- attributionTagFilter)) {
- continue;
- }
List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
attributionTag);
int innerDepth = parser.getDepth();
@@ -655,11 +760,7 @@
-1);
int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
- if ((flagsFilter & opFlags) == 0) {
- continue;
- }
- if ((noteTime + noteDuration < beginTimeMillis
- && noteTime > endTimeMillis)) {
+ if (noteTime + noteDuration < beginTimeMillis) {
continue;
}
DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
@@ -715,5 +816,41 @@
out.attributeInt(null, ATTR_FLAGS, mOpFlag);
}
}
+
+ private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
+ List<DiscreteOpEvent> b) {
+ int nA = a.size();
+ int nB = b.size();
+ int i = 0;
+ int k = 0;
+ List<DiscreteOpEvent> result = new ArrayList<>(nA + nB);
+ while (i < nA || k < nB) {
+ if (i == nA) {
+ result.add(b.get(k++));
+ } else if (k == nB) {
+ result.add(a.get(i++));
+ } else if (a.get(i).mNoteTime < b.get(k).mNoteTime) {
+ result.add(a.get(i++));
+ } else {
+ result.add(b.get(k++));
+ }
+ }
+ return result;
+ }
+
+ private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
+ long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+ int n = list.size();
+ List<DiscreteOpEvent> result = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ DiscreteOpEvent event = list.get(i);
+ if ((event.mOpFlag & flagsFilter) != 0
+ && event.mNoteTime + event.mNoteDuration > beginTimeMillis
+ && event.mNoteTime < endTimeMillis) {
+ result.add(event);
+ }
+ }
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index eeed452..4435c47 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -532,7 +532,7 @@
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, increment, eventStartTime);
+ flags, uidState, eventStartTime, increment);
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 672ed3d..6ab4a69 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2621,9 +2621,8 @@
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
// Dispatch display id for InputMethodService to update context display.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
- MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
- mMethodMap.get(mCurMethodId).getConfigChanges()));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
+ MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
scheduleNotifyImeUidToAudioService(mCurMethodUid);
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -4479,8 +4478,7 @@
}
final IBinder token = (IBinder) args.arg2;
((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
- new InputMethodPrivilegedOperationsImpl(this, token),
- (int) args.arg3);
+ new InputMethodPrivilegedOperationsImpl(this, token));
} catch (RemoteException e) {
}
args.recycle();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0cc9f9e..d5a9e3c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7376,15 +7376,7 @@
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(record.getKey()) != null) {
- // Vibrator checks the appops for the op package, not the caller,
- // so we need to add the bypass dnd flag to be heard. it's ok to
- // always add this flag here because we've already checked that we can
- // bypass dnd
- AudioAttributes.Builder aab =
- new AudioAttributes.Builder(record.getAudioAttributes())
- .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
- mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
- effect, "Notification (delayed)", aab.build());
+ vibrate(record, effect, true);
} else {
Slog.e(TAG, "No vibration for canceled notification : "
+ record.getKey());
@@ -7392,8 +7384,7 @@
}
}).start();
} else {
- mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
- effect, "Notification", record.getAudioAttributes());
+ vibrate(record, effect, false);
}
return true;
} finally{
@@ -7401,6 +7392,16 @@
}
}
+ private void vibrate(NotificationRecord record, VibrationEffect effect, boolean delayed) {
+ // We need to vibrate as "android" so we can breakthrough DND. VibratorManagerService
+ // doesn't have a concept of vibrating on an app's behalf, so add the app information
+ // to the reason so we can still debug from bugreports
+ String reason = "Notification (" + record.getSbn().getOpPkg() + " "
+ + record.getSbn().getUid() + ") " + (delayed ? "(Delayed)" : "");
+ mVibrator.vibrate(Process.SYSTEM_UID, PackageManagerService.PLATFORM_PACKAGE_NAME,
+ effect, reason, record.getAudioAttributes());
+ }
+
private boolean isNotificationForCurrentUser(NotificationRecord record) {
final int currentUser;
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index d4eedf1..52d110c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -557,7 +557,7 @@
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
- boolean mSupportsNonResizableMultiWindow;
+ volatile boolean mSupportsNonResizableMultiWindow;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -3432,6 +3432,11 @@
}
@Override
+ public boolean supportsNonResizableMultiWindow() {
+ return mSupportsNonResizableMultiWindow;
+ }
+
+ @Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48ae8d6..aed13b2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -19,6 +19,8 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -38,7 +40,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -455,8 +456,7 @@
try {
trustAgentInfo.options.saveToXml(out);
} catch (XmlPullParserException e) {
- Log.e(DevicePolicyManagerService.LOG_TAG,
- "Failed to save TrustAgent options", e);
+ Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
}
out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
}
@@ -629,8 +629,7 @@
String tag = parser.getName();
if (TAG_POLICIES.equals(tag)) {
if (shouldOverridePolicies) {
- Log.d(DevicePolicyManagerService.LOG_TAG,
- "Overriding device admin policies from XML.");
+ Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
info.readPoliciesFromXml(parser);
}
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -726,16 +725,14 @@
if (type == TypedXmlPullParser.TEXT) {
shortSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading short support message");
+ Slog.w(LOG_TAG, "Missing text when loading short support message");
}
} else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
longSupportMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading long support message");
+ Slog.w(LOG_TAG, "Missing text when loading long support message");
}
} else if (TAG_PARENT_ADMIN.equals(tag)) {
Preconditions.checkState(!isParent);
@@ -748,8 +745,7 @@
if (type == TypedXmlPullParser.TEXT) {
organizationName = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading organization name");
+ Slog.w(LOG_TAG, "Missing text when loading organization name");
}
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -758,16 +754,14 @@
if (type == TypedXmlPullParser.TEXT) {
startUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading start session message");
+ Slog.w(LOG_TAG, "Missing text when loading start session message");
}
} else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
endUserSessionMessage = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing text when loading end session message");
+ Slog.w(LOG_TAG, "Missing text when loading end session message");
}
} else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -802,16 +796,14 @@
if (type == TypedXmlPullParser.TEXT) {
mOrganizationId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Organization ID.");
+ Slog.w(LOG_TAG, "Missing Organization ID.");
}
} else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
type = parser.next();
if (type == TypedXmlPullParser.TEXT) {
mEnrollmentSpecificId = parser.getText();
} else {
- Log.w(DevicePolicyManagerService.LOG_TAG,
- "Missing Enrollment-specific ID.");
+ Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
}
} else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -820,7 +812,7 @@
mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
USB_DATA_SIGNALING_ENABLED_DEFAULT);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
+ Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -842,12 +834,10 @@
if (packageName != null) {
result.add(packageName);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Package name missing under " + outerTag);
+ Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
}
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + outerTag);
+ Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
}
}
return result;
@@ -868,8 +858,7 @@
if (tag.equals(tagDAM)) {
result.add(parser.getAttributeValue(null, ATTR_VALUE));
} else {
- Slog.e(DevicePolicyManagerService.LOG_TAG,
- "Expected tag " + tag + " but found " + tagDAM);
+ Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
}
}
}
@@ -891,8 +880,7 @@
final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
result.put(component, trustAgentInfo);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
@@ -912,8 +900,7 @@
if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
result.options = PersistableBundle.restoreFromXml(parser);
} else {
- Slog.w(DevicePolicyManagerService.LOG_TAG,
- "Unknown tag under " + tag + ": " + tagDAM);
+ Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index d812b8f..e0c5e32 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -33,7 +35,7 @@
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -47,7 +49,6 @@
import java.util.List;
public class CertificateMonitor {
- protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
protected static final int MONITORING_CERT_NOTIFICATION_ID = SystemMessage.NOTE_SSL_CERT_INFO;
private final DevicePolicyManagerService mService;
@@ -78,16 +79,16 @@
X509Certificate cert = parseCert(certBuffer);
pemCert = Credentials.convertToPem(cert);
} catch (CertificateException | IOException ce) {
- Log.e(LOG_TAG, "Problem converting cert", ce);
+ Slog.e(LOG_TAG, ce, "Problem converting cert");
return null;
}
try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
return keyChainConnection.getService().installCaCertificate(pemCert);
} catch (RemoteException e) {
- Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+ Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): ");
} catch (InterruptedException e1) {
- Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+ Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): ");
Thread.currentThread().interrupt();
}
return null;
@@ -99,9 +100,9 @@
keyChainConnection.getService().deleteCaCertificate(aliases[i]);
}
} catch (RemoteException e) {
- Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
+ Slog.e(LOG_TAG, e, "from CaCertUninstaller: ");
} catch (InterruptedException ie) {
- Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
+ Slog.w(LOG_TAG, ie, "CaCertUninstaller: ");
Thread.currentThread().interrupt();
}
}
@@ -137,7 +138,8 @@
};
private void updateInstalledCertificates(final UserHandle userHandle) {
- if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
+ final int userId = userHandle.getIdentifier();
+ if (!mInjector.getUserManager().isUserUnlocked(userId)) {
return;
}
@@ -145,7 +147,8 @@
try {
installedCerts = getInstalledCaCertificates(userHandle);
} catch (RemoteException | RuntimeException e) {
- Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+ userId);
return;
}
mService.onInstalledCertificatesChanged(userHandle, installedCerts);
@@ -167,7 +170,7 @@
try {
userContext = mInjector.createContextAsUser(userHandle);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
+ Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
return null;
}
@@ -183,7 +186,6 @@
smallIconId = R.drawable.stat_sys_certificate_info;
parentUserId = mService.getProfileParentId(userHandle.getIdentifier());
} else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) {
- final String ownerName = mService.getDeviceOwnerName();
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getDeviceOwnerName());
smallIconId = R.drawable.stat_sys_certificate_info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 3067d45..00e0292 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -46,19 +46,11 @@
final Object mLock = new Object();
final Context mContext;
- private final DevicePolicyManagerService mService;
private final DevicePolicyManagerService.Injector mInjector;
private final DevicePolicyConstants mConstants;
private final Handler mHandler; // needed?
- static void debug(String format, Object... args) {
- if (!DEBUG) {
- return;
- }
- Slog.d(TAG, String.format(format, args));
- }
-
private class DevicePolicyServiceConnection
extends PersistentConnection<IDeviceAdminService> {
public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) {
@@ -88,7 +80,6 @@
public DeviceAdminServiceController(DevicePolicyManagerService service,
DevicePolicyConstants constants) {
- mService = service;
mInjector = service.mInjector;
mContext = mInjector.mContext;
mHandler = new Handler(BackgroundThread.get().getLooper());
@@ -122,8 +113,9 @@
synchronized (mLock) {
final ServiceInfo service = findService(packageName, userId);
if (service == null) {
- debug("Owner package %s on u%d has no service.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
return;
}
@@ -134,14 +126,17 @@
// Note even when we're already connected to the same service, the binding
// would have died at this point due to a package update. So we disconnect
// anyway and re-connect.
- debug("Disconnecting from existing service connection.",
- packageName, userId);
+ if (DEBUG) {
+ Slog.d("Disconnecting from existing service connection.", packageName,
+ userId);
+ }
disconnectServiceOnUserLocked(userId, actionForLog);
}
- debug("Owner package %s on u%d has service %s for %s",
- packageName, userId,
+ if (DEBUG) {
+ Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
service.getComponentName().flattenToShortString(), actionForLog);
+ }
final DevicePolicyServiceConnection conn =
new DevicePolicyServiceConnection(
@@ -172,8 +167,10 @@
private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
final DevicePolicyServiceConnection conn = mConnections.get(userId);
if (conn != null) {
- debug("Stopping service for u%d if already running for %s.",
- userId, actionForLog);
+ if (DEBUG) {
+ Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+ actionForLog);
+ }
conn.unbind();
mConnections.remove(userId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 464d6f5..84e6da0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -88,7 +88,7 @@
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
- Slog.e(TAG, "Bad device policy settings: " + settings);
+ Slog.e(TAG, "Bad device policy settings: %s", settings);
}
long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index c0b2ed4..52cdce6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -179,11 +179,11 @@
*/
static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) {
FileOutputStream stream = null;
+ File chooseForWrite = null;
try {
- File chooseForWrite = file.chooseForWrite();
+ chooseForWrite = file.chooseForWrite();
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing data for user " + policyData.mUserId + " on "
- + chooseForWrite);
+ Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
}
stream = new FileOutputStream(chooseForWrite, false);
TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@
policyData.mRestrictionsProvider.flattenToString());
}
if (policyData.mUserSetupComplete) {
- if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
+ if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
}
if (policyData.mPaired) {
@@ -216,8 +216,8 @@
if (policyData.mFactoryResetFlags != 0) {
if (VERBOSE_LOG) {
- Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": "
- + factoryResetFlagsToString(policyData.mFactoryResetFlags));
+ Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+ factoryResetFlagsToString(policyData.mFactoryResetFlags));
}
out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
}
@@ -382,7 +382,7 @@
file.commit();
return true;
} catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "failed writing file", e);
+ Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
try {
if (stream != null) {
stream.close();
@@ -404,10 +404,8 @@
ComponentName ownerComponent) {
FileInputStream stream = null;
File file = journaledFile.chooseForRead();
- if (VERBOSE_LOG) {
- Slog.v(TAG, "Loading data for user " + policy.mUserId + " from " + file);
- }
-
+ if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+ boolean needsRewrite = false;
try {
stream = new FileInputStream(file);
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -454,8 +452,8 @@
policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
if (VERBOSE_LOG) {
- Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": "
- + factoryResetFlagsToString(policy.mFactoryResetFlags));
+ Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+ factoryResetFlagsToString(policy.mFactoryResetFlags));
}
policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
@@ -488,7 +486,7 @@
policy.mAdminMap.put(ap.info.getComponent(), ap);
}
} catch (RuntimeException e) {
- Slog.w(TAG, "Failed loading admin " + name, e);
+ Slog.w(TAG, e, "Failed loading admin %s", name);
}
} else if ("delegation".equals(tag)) {
// Parse delegation info.
@@ -560,7 +558,7 @@
policy.mAppsSuspended =
parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else {
- Slog.w(TAG, "Unknown tag: " + tag);
+ Slog.w(TAG, "Unknown tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
}
}
@@ -568,7 +566,7 @@
// Don't be noisy, this is normal if we haven't defined any policies.
} catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
| IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + file, e);
+ Slog.w(TAG, e, "failed parsing %s", file);
}
try {
if (stream != null) {
@@ -592,8 +590,8 @@
}
}
if (!haveOwner) {
- Slog.w(TAG, "Previous password owner " + mPasswordOwner
- + " no longer active; disabling");
+ Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+ mPasswordOwner);
mPasswordOwner = -1;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2855c70..577f3f5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1106,7 +1106,7 @@
* Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
*/
void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
- Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker));
+ Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
mSafetyChecker = safetyChecker;
mInjector.setDevicePolicySafetyChecker(safetyChecker);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 457255b..28a6987 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -68,17 +68,16 @@
IResultReceiver receiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
- Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding",
- mSafetyChecker));
+ Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
try {
factoryResetInternalUnchecked();
} catch (IOException e) {
// Shouldn't happen
- Slog.wtf(TAG, "IOException calling underlying systems", e);
+ Slog.wtf(TAG, e, "IOException calling underlying systems");
}
}
};
- Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
+ Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
mSafetyChecker.onFactoryReset(receiver);
return false;
}
@@ -113,9 +112,9 @@
}
private void factoryResetInternalUnchecked() throws IOException {
- Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+ "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
- mWipeAdoptableStorage, mWipeFactoryResetProtection));
+ mWipeAdoptableStorage, mWipeFactoryResetProtection);
UserManager um = mContext.getSystemService(UserManager.class);
if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 37dbfc1..0b9ece4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -18,6 +18,8 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -36,6 +38,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -105,7 +108,9 @@
result.remove(pkg);
}
- Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
+ if (Log.isLoggable(LOG_TAG, Log.INFO)) {
+ Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+ }
return result.toArray(new String[0]);
}
@@ -118,7 +123,7 @@
for (final ResolveInfo resolveInfo : matchingActivities) {
if (resolveInfo.activityInfo == null
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
- Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
+ Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
continue;
}
final String packageName = resolveInfo.activityInfo.packageName;
@@ -129,7 +134,8 @@
result.add(packageName);
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
+ Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+ packageName);
}
}
return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 543f381..2959c10 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -25,6 +25,8 @@
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
@@ -58,7 +60,6 @@
* Class managing bugreport collection upon device owner's request.
*/
public class RemoteBugreportManager {
- private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
@@ -206,7 +207,7 @@
return true;
} catch (RemoteException re) {
// should never happen
- Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+ Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service");
return false;
} finally {
mInjector.binderRestoreCallingIdentity(callingIdentity);
@@ -220,7 +221,7 @@
mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
} catch (IntentFilter.MalformedMimeTypeException e) {
// should never happen, as setting a constant
- Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+ Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
}
final IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index 129d263..e13597d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -19,23 +19,37 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.ActivityManager;
+import android.app.ActivityManager.OnUidImportanceListener;
import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -65,6 +79,12 @@
private static final long AWAIT_TIMEOUT = 2000;
private static final long CHECK_INTERVAL = 100;
+ private static final String TEST_FGS_CLASS =
+ "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
private IActivityManager mService;
private IRemoteCallback mCallback;
private Context mContext;
@@ -204,4 +224,184 @@
public void onServiceDisconnected(ComponentName name) {
}
}
+
+ /**
+ * Note: This test actually only works in eng build. It'll always pass
+ * in user and userdebug build, because the expected exception won't be
+ * thrown in those builds.
+ */
+ @LargeTest
+ @Test
+ public void testFgsProcStatsTracker() throws Exception {
+ final PackageManager pm = mContext.getPackageManager();
+ final long timeout = 5000;
+ int uid = pm.getPackageUid(TEST_APP, 0);
+ final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
+ final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final H handler = new H(Looper.getMainLooper(), latchHolder);
+ final Messenger messenger = new Messenger(handler);
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ final CountDownLatch dboxLatch = new CountDownLatch(1);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String tag_wtf = "system_server_wtf";
+ if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
+ final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
+ DropBoxManager.EXTRA_TIME, 0) - 1);
+ final String text = e.getText(8192);
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+ if (text.indexOf("can't store negative values") == -1) {
+ return;
+ }
+ dboxLatch.countDown();
+ }
+ }
+ };
+ try {
+ mContext.registerReceiver(receiver,
+ new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
+ am.addOnUidImportanceListener(uidListener1,
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ runShellCommand("cmd deviceidle whitelist +" + TEST_APP);
+ toggleScreenOn(true);
+
+ final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
+ final ComponentName cn = ComponentName.unflattenFromString(
+ TEST_APP + "/" + TEST_FGS_CLASS);
+ final Bundle bundle = new Bundle();
+ intent.setComponent(cn);
+ bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
+ intent.putExtras(bundle);
+
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ Thread.sleep(timeout);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for start fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+
+ toggleScreenOn(false);
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ timeout, TimeUnit.MILLISECONDS));
+ assertFalse("There shouldn't be negative values", dboxLatch.await(
+ timeout * 2, TimeUnit.MILLISECONDS));
+ } finally {
+ toggleScreenOn(true);
+ runShellCommand("cmd deviceidle whitelist -" + TEST_APP);
+ am.removeOnUidImportanceListener(uidListener1);
+ am.removeOnUidImportanceListener(uidListener2);
+ am.forceStopPackage(TEST_APP);
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /**
+ * Make sure the screen state.
+ */
+ private void toggleScreenOn(final boolean screenon) throws Exception {
+ if (screenon) {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ runShellCommand("wm dismiss-keyguard");
+ } else {
+ runShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(2_000);
+ }
+
+ private class H extends Handler {
+ static final int MSG_INIT = 0;
+ static final int MSG_DONE = 1;
+ static final int MSG_START_FOREGROUND = 2;
+ static final int MSG_STOP_FOREGROUND = 3;
+
+ private Messenger mRemoteMessenger;
+ private CountDownLatch[] mLatchHolder;
+
+ H(Looper looper, CountDownLatch[] latchHolder) {
+ super(looper);
+ mLatchHolder = latchHolder;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT:
+ mRemoteMessenger = (Messenger) msg.obj;
+ mLatchHolder[0].countDown();
+ break;
+ case MSG_DONE:
+ mLatchHolder[0].countDown();
+ break;
+ }
+ }
+
+ void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ msg.recycle();
+ }
+ }
+
+ private static class MyUidImportanceListener implements OnUidImportanceListener {
+ final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
+ private final int mExpectedUid;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+ MyUidImportanceListener(int uid) {
+ mExpectedUid = uid;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ synchronized (this) {
+ if (importance == mExpectedImportance && mLatchHolder[0] != null) {
+ mLatchHolder[0].countDown();
+ }
+ mCurrentImportance = importance;
+ }
+ Log.i(TAG, "uid " + uid + " importance: " + importance);
+ }
+ }
+
+ boolean waitFor(int expectedImportance, long timeout) throws Exception {
+ synchronized (this) {
+ mExpectedImportance = expectedImportance;
+ if (mCurrentImportance == expectedImportance) {
+ return true;
+ }
+ mLatchHolder[0] = new CountDownLatch(1);
+ }
+ return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
+ }
+ }
}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 8789992..799ec53 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -17,9 +17,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.servicestests.apps.simpleservicetestapp">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
<application>
<service android:name=".SimpleService"
android:exported="true" />
+ <service android:name=".SimpleFgService"
+ android:exported="true" />
</application>
</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
new file mode 100644
index 0000000..ccfc0b7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+package com.android.servicestests.apps.simpleservicetestapp;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.R;
+
+public class SimpleFgService extends Service {
+ private static final String TAG = SimpleFgService.class.getSimpleName();
+ private static final String NOTIFICATION_CHANNEL_ID = TAG;
+ private static final int NOTIFICATION_ID = 1;
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_DONE = 1;
+ private static final int MSG_START_FOREGROUND = 2;
+ private static final int MSG_STOP_FOREGROUND = 3;
+
+ private static final String ACTION_FGS_STATS_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
+ private static final String EXTRA_MESSENGER = "extra_messenger";
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_FOREGROUND: {
+ Log.i(TAG, "startForeground");
+ startForeground(NOTIFICATION_ID, mNotification);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ case MSG_STOP_FOREGROUND: {
+ Log.i(TAG, "stopForeground");
+ stopForeground(true);
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
+ }
+ }
+ };
+ private final Messenger mMessenger = new Messenger(mHandler);
+
+ private Notification mNotification;
+ private Messenger mRemoteMessenger;
+
+ @Override
+ public void onCreate() {
+ Log.i(TAG, "onCreate");
+ final NotificationManager nm = getSystemService(NotificationManager.class);
+ nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
+ mNotification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(TAG)
+ .setSmallIcon(R.drawable.ic_info)
+ .build();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand");
+ startForeground(NOTIFICATION_ID, mNotification);
+ if (ACTION_FGS_STATS_TEST.equals(intent.getAction())) {
+ mRemoteMessenger = new Messenger(intent.getExtras().getBinder(EXTRA_MESSENGER));
+ sendRemoteMessage(MSG_INIT, 0, 0, mMessenger);
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
+ final Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ try {
+ mRemoteMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy");
+ mNotification = null;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index e510b4f..5462f47 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -61,6 +61,7 @@
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationEffect;
@@ -81,10 +82,12 @@
import com.android.internal.util.IntPair;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LogicalLight;
+import com.android.server.pm.PackageManagerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -412,12 +415,16 @@
}
private void verifyVibrate() {
+ ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
- anyString(), any(AudioAttributes.class));
+ anyString(), captor.capture());
+ assertEquals(0, (captor.getValue().getAllFlags()
+ & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
}
private void verifyVibrate(int times) {
- verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(),
+ verify(mVibrator, times(times)).vibrate(eq(Process.SYSTEM_UID),
+ eq(PackageManagerService.PLATFORM_PACKAGE_NAME), any(), anyString(),
any(AudioAttributes.class));
}
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index 2e985fb..b9b347b 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -43,7 +43,7 @@
xPrecision, yPrecision, deviceId, edgeFlags, source, displayId)
}
-fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
+private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent {
val code = KeyEvent.KEYCODE_A
val repeat = 0
return KeyEvent(eventTime, eventTime, action, code, repeat)
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
new file mode 100644
index 0000000..4f95ce5
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package com.android.test.input
+
+import android.os.HandlerThread
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.CountDownLatch
+import org.junit.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
+ assertEquals(expected.action, received.action)
+ assertEquals(expected.deviceId, received.deviceId)
+ assertEquals(expected.downTime, received.downTime)
+ assertEquals(expected.eventTime, received.eventTime)
+ assertEquals(expected.keyCode, received.keyCode)
+ assertEquals(expected.scanCode, received.scanCode)
+ assertEquals(expected.repeatCount, received.repeatCount)
+ assertEquals(expected.metaState, received.metaState)
+ assertEquals(expected.flags, received.flags)
+ assertEquals(expected.source, received.source)
+ assertEquals(expected.displayId, received.displayId)
+}
+
+class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+ InputEventReceiver(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventReceiver"
+ }
+
+ var lastEvent: InputEvent? = null
+
+ override fun onInputEvent(event: InputEvent) {
+ lastEvent = when (event) {
+ is KeyEvent -> KeyEvent.obtain(event)
+ is MotionEvent -> MotionEvent.obtain(event)
+ else -> throw Exception("Received $event is neither a key nor a motion")
+ }
+ finishInputEvent(event, true /*handled*/)
+ }
+}
+
+class TestInputEventSender(channel: InputChannel, looper: Looper) :
+ InputEventSender(channel, looper) {
+ companion object {
+ const val TAG = "TestInputEventSender"
+ }
+ data class FinishedResult(val seq: Int, val handled: Boolean)
+
+ private var mFinishedSignal = CountDownLatch(1)
+ override fun onInputEventFinished(seq: Int, handled: Boolean) {
+ finishedResult = FinishedResult(seq, handled)
+ mFinishedSignal.countDown()
+ }
+ lateinit var finishedResult: FinishedResult
+
+ fun waitForFinish() {
+ mFinishedSignal.await()
+ mFinishedSignal = CountDownLatch(1) // Ready for next event
+ }
+}
+
+class InputEventSenderAndReceiverTest {
+ companion object {
+ private const val TAG = "InputEventSenderAndReceiverTest"
+ }
+ private val mHandlerThread = HandlerThread("Process input events")
+ private lateinit var mReceiver: TestInputEventReceiver
+ private lateinit var mSender: TestInputEventSender
+
+ @Before
+ fun setUp() {
+ val channels = InputChannel.openInputChannelPair("TestChannel")
+ mHandlerThread.start()
+
+ val looper = mHandlerThread.getLooper()
+ mSender = TestInputEventSender(channels[0], looper)
+ mReceiver = TestInputEventReceiver(channels[1], looper)
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ }
+
+ @Test
+ fun testSendAndReceiveKey() {
+ val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ val seq = 10
+ mSender.sendInputEvent(seq, key)
+ mSender.waitForFinish()
+
+ // Check receiver
+ assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent)
+
+ // Check sender
+ assertEquals(seq, mSender.finishedResult.seq)
+ assertEquals(true, mSender.finishedResult.handled)
+ }
+}