Merge "Improved dumpsys device_policy:"
diff --git a/Android.bp b/Android.bp
index 6b55cc9..c90e00c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -580,6 +580,7 @@
aidl: {
generate_get_transaction_name: true,
local_include_dirs: ["media/aidl"],
+ include_dirs: ["frameworks/av/aidl"],
},
dxflags: [
"--core-library",
@@ -596,6 +597,7 @@
"framework-platform-compat-config",
// TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
"gps_debug.conf",
+ "icu4j-platform-compat-config",
"libcore-platform-compat-config",
"protolog.conf.json.gz",
"services-platform-compat-config",
@@ -614,6 +616,7 @@
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
+ "av-types-aidl-java",
"mediatranscoding_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index faa0e5d..7ed7ec5 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -86,6 +86,7 @@
// TODO(b/169090544): remove below aidl includes.
aidl: {
local_include_dirs: ["media/aidl"],
+ include_dirs: ["frameworks/av/aidl"],
},
}
@@ -157,6 +158,7 @@
// TODO(b/169090544): remove below aidl includes.
aidl: {
local_include_dirs: ["media/aidl"],
+ include_dirs: ["frameworks/av/aidl"],
},
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 8090bec..228b3da 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -57,6 +57,7 @@
"telephony/java",
"media/aidl",
],
+ include_dirs: ["frameworks/av/aidl"],
},
// These are libs from framework-internal-utils that are required (i.e. being referenced)
// from framework-non-updatable-sources. Add more here when there's a need.
@@ -338,19 +339,6 @@
"framework-wifi.stubs",
"private-stub-annotations-jar",
],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_stubs_current",
- static_libs: ["android_merged_stubs_current"],
- defaults: ["android_defaults_stubs_current"],
-}
-
-java_library_static {
- name: "android_system_monolith_stubs_current",
- srcs: [ ":system-api-stubs-docs" ],
- static_libs: [ "private-stub-annotations-jar" ],
defaults: [
"android_defaults_stubs_current",
"android_stubs_dists_default",
@@ -369,6 +357,21 @@
}
java_library_static {
+ name: "android_stubs_current",
+ static_libs: ["android_merged_stubs_current"],
+ defaults: ["android_defaults_stubs_current"],
+}
+
+java_library_static {
+ name: "android_system_monolith_stubs_current",
+ srcs: [ ":system-api-stubs-docs" ],
+ static_libs: [ "private-stub-annotations-jar" ],
+ defaults: [
+ "android_defaults_stubs_current",
+ ],
+}
+
+java_library_static {
name: "android_system_merged_stubs_current",
srcs: [ ":system-api-stubs-docs-non-updatable" ],
static_libs: [
diff --git a/apex/Android.bp b/apex/Android.bp
index c5b4901..0a535a8 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -112,6 +112,8 @@
],
stubs_source_visibility: ["//visibility:private"],
+ defaults_visibility: ["//visibility:private"],
+
// Collates API usages from each module for further analysis.
plugins: ["java_api_finder"],
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index c8a04d6..ba2a8a3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -268,6 +268,7 @@
*/
Bundle mIdleOptions;
+ // TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>();
private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -276,6 +277,9 @@
new SparseBooleanArray();
private boolean mNextAlarmClockMayChange;
+ @GuardedBy("mLock")
+ private final Runnable mAlarmClockUpdater = () -> mNextAlarmClockMayChange = true;
+
// May only use on mHandler's thread, locking not required.
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
new SparseArray<>();
@@ -410,6 +414,9 @@
private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW =
"app_standby_restricted_window";
+ @VisibleForTesting
+ static final String KEY_LAZY_BATCHING = "lazy_batching";
+
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
@@ -432,6 +439,8 @@
private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1;
private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;
+ private static final boolean DEFAULT_LAZY_BATCHING = false;
+
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -460,6 +469,8 @@
public int APP_STANDBY_RESTRICTED_QUOTA = DEFAULT_APP_STANDBY_RESTRICTED_QUOTA;
public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW;
+ public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
Constants() {
@@ -538,6 +549,14 @@
case KEY_APP_STANDBY_RESTRICTED_WINDOW:
updateStandbyWindowsLocked();
break;
+ case KEY_LAZY_BATCHING:
+ final boolean oldLazyBatching = LAZY_BATCHING;
+ LAZY_BATCHING = properties.getBoolean(
+ KEY_LAZY_BATCHING, DEFAULT_LAZY_BATCHING);
+ if (oldLazyBatching != LAZY_BATCHING) {
+ migrateAlarmsToNewStoreLocked();
+ }
+ break;
default:
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
// The quotas need to be updated in order, so we can't just rely
@@ -551,6 +570,15 @@
}
}
+ private void migrateAlarmsToNewStoreLocked() {
+ final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore()
+ : new BatchingAlarmStore();
+ final ArrayList<Alarm> allAlarms = mAlarmStore.remove((unused) -> true);
+ newStore.addAll(allAlarms);
+ mAlarmStore = newStore;
+ mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
+ }
+
private void updateStandbyQuotasLocked() {
// The bucket quotas need to be read as an atomic unit but the properties passed to
// onPropertiesChanged may only have one key populated at a time.
@@ -659,6 +687,9 @@
TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw);
pw.println();
+ pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING);
+ pw.println();
+
pw.decreaseIndent();
}
@@ -770,7 +801,7 @@
// minimum recurrence period or alarm futurity for us to be able to fuzz it
static final long MIN_FUZZABLE_INTERVAL = 10000;
@GuardedBy("mLock")
- final AlarmStore mAlarmStore;
+ AlarmStore mAlarmStore;
// set to non-null if in idle mode; while in this mode, any alarms we don't want
// to run during this time are rescehduled to go off after this alarm.
@@ -781,7 +812,6 @@
AlarmManagerService(Context context, Injector injector) {
super(context);
mInjector = injector;
- mAlarmStore = new BatchingAlarmStore(() -> mNextAlarmClockMayChange = true);
}
public AlarmManagerService(Context context) {
@@ -1219,6 +1249,11 @@
synchronized (mLock) {
mHandler = new AlarmHandler();
mConstants = new Constants();
+
+ mAlarmStore = mConstants.LAZY_BATCHING ? new LazyAlarmStore()
+ : new BatchingAlarmStore();
+ mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater);
+
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -3055,12 +3090,13 @@
static final void dumpAlarmList(IndentingPrintWriter ipw, ArrayList<Alarm> list,
long nowELAPSED, SimpleDateFormat sdf) {
- for (int i = list.size() - 1; i >= 0; i--) {
+ final int n = list.size();
+ for (int i = n - 1; i >= 0; i--) {
final Alarm a = list.get(i);
final String label = Alarm.typeToString(a.type);
ipw.print(label);
ipw.print(" #");
- ipw.print(i);
+ ipw.print(n - i);
ipw.print(": ");
ipw.println(a);
ipw.increaseIndent();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
index 7a846b9..0e442d0 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -40,6 +40,13 @@
void add(Alarm a);
/**
+ * Adds all the given alarms to this store.
+ *
+ * @param alarms The alarms to add.
+ */
+ void addAll(ArrayList<Alarm> alarms);
+
+ /**
* Removes alarms that pass the given predicate.
*
* @param whichAlarms The predicate describing the alarms to remove.
@@ -48,11 +55,17 @@
ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms);
/**
+ * Set a listener to be invoked whenever an alarm clock is removed by a call to
+ * {@link #remove(Predicate) remove} from this store.
+ */
+ void setAlarmClockRemovalListener(Runnable listener);
+
+ /**
* Gets the earliest alarm with the flag {@link android.app.AlarmManager#FLAG_WAKE_FROM_IDLE}
* based on {@link Alarm#getWhenElapsed()}.
*
* @return An alarm object matching the description above or {@code null} if no such alarm was
- * found.
+ * found.
*/
Alarm getNextWakeFromIdleAlarm();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index cbfe80b..e7edfb7 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -41,45 +41,22 @@
*/
public class BatchingAlarmStore implements AlarmStore {
- private ArrayList<Batch> mAlarmBatches = new ArrayList<>();
+ private final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
private int mSize;
- private AlarmClockRemovalListener mAlarmClockRemovalListener;
+ private Runnable mOnAlarmClockRemoved;
interface Stats {
int REBATCH_ALL_ALARMS = 0;
}
- final StatLogger mStatLogger = new StatLogger("Alarm store stats", new String[]{
+ final StatLogger mStatLogger = new StatLogger("BatchingAlarmStore stats", new String[]{
"REBATCH_ALL_ALARMS",
});
- private static final Comparator<Batch> sBatchOrder = (b1, b2) -> {
- long when1 = b1.mStart;
- long when2 = b2.mStart;
- if (when1 > when2) {
- return 1;
- }
- if (when1 < when2) {
- return -1;
- }
- return 0;
- };
+ private static final Comparator<Batch> sBatchOrder = Comparator.comparingLong(b -> b.mStart);
- private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> {
- long when1 = a1.getWhenElapsed();
- long when2 = a2.getWhenElapsed();
- if (when1 > when2) {
- return 1;
- }
- if (when1 < when2) {
- return -1;
- }
- return 0;
- };
-
- BatchingAlarmStore(AlarmClockRemovalListener listener) {
- mAlarmClockRemovalListener = listener;
- }
+ private static final Comparator<Alarm> sIncreasingTimeOrder = Comparator.comparingLong(
+ Alarm::getWhenElapsed);
@Override
public void add(Alarm a) {
@@ -88,6 +65,16 @@
}
@Override
+ public void addAll(ArrayList<Alarm> alarms) {
+ if (alarms == null) {
+ return;
+ }
+ for (final Alarm a : alarms) {
+ add(a);
+ }
+ }
+
+ @Override
public ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms) {
final ArrayList<Alarm> removed = new ArrayList<>();
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
@@ -106,6 +93,11 @@
}
@Override
+ public void setAlarmClockRemovalListener(Runnable listener) {
+ mOnAlarmClockRemoved = listener;
+ }
+
+ @Override
public Alarm getNextWakeFromIdleAlarm() {
for (final Batch batch : mAlarmBatches) {
if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) {
@@ -317,8 +309,8 @@
Alarm alarm = mAlarms.get(i);
if (predicate.test(alarm)) {
removed.add(mAlarms.remove(i));
- if (alarm.alarmClock != null && mAlarmClockRemovalListener != null) {
- mAlarmClockRemovalListener.onRemoved();
+ if (alarm.alarmClock != null && mOnAlarmClockRemoved != null) {
+ mOnAlarmClockRemoved.run();
}
if (isTimeTickAlarm(alarm)) {
// This code path is not invoked when delivering alarms, only when removing
@@ -388,9 +380,4 @@
proto.end(token);
}
}
-
- @FunctionalInterface
- interface AlarmClockRemovalListener {
- void onRemoved();
- }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
new file mode 100644
index 0000000..8ca1446
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -0,0 +1,217 @@
+/*
+ * 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.server.alarm;
+
+import static com.android.server.alarm.AlarmManagerService.TAG;
+import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
+import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
+
+import android.app.AlarmManager;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.StatLogger;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Lazy implementation of an alarm store.
+ * This keeps the alarms in a sorted list, and only batches them at the time of delivery.
+ */
+public class LazyAlarmStore implements AlarmStore {
+
+ private final ArrayList<Alarm> mAlarms = new ArrayList<>();
+ private Runnable mOnAlarmClockRemoved;
+
+ interface Stats {
+ int GET_NEXT_DELIVERY_TIME = 0;
+ int GET_NEXT_WAKEUP_DELIVERY_TIME = 1;
+ }
+
+ final StatLogger mStatLogger = new StatLogger("LazyAlarmStore stats", new String[]{
+ "GET_NEXT_DELIVERY_TIME",
+ "GET_NEXT_WAKEUP_DELIVERY_TIME",
+ });
+
+ // Decreasing time order because it is more efficient to remove from the tail of an array list.
+ private static final Comparator<Alarm> sDecreasingTimeOrder = Comparator.comparingLong(
+ Alarm::getWhenElapsed).reversed();
+
+ @Override
+ public void add(Alarm a) {
+ int index = Collections.binarySearch(mAlarms, a, sDecreasingTimeOrder);
+ if (index < 0) {
+ index = 0 - index - 1;
+ }
+ mAlarms.add(index, a);
+ }
+
+ @Override
+ public void addAll(ArrayList<Alarm> alarms) {
+ if (alarms == null) {
+ return;
+ }
+ mAlarms.addAll(alarms);
+ Collections.sort(alarms, sDecreasingTimeOrder);
+ }
+
+ @Override
+ public ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms) {
+ final ArrayList<Alarm> removedAlarms = new ArrayList<>();
+ for (int i = mAlarms.size() - 1; i >= 0; i--) {
+ if (whichAlarms.test(mAlarms.get(i))) {
+ final Alarm removed = mAlarms.remove(i);
+ if (removed.alarmClock != null && mOnAlarmClockRemoved != null) {
+ mOnAlarmClockRemoved.run();
+ }
+ if (isTimeTickAlarm(removed)) {
+ // This code path is not invoked when delivering alarms, only when removing
+ // alarms due to the caller cancelling it or getting uninstalled, etc.
+ Slog.wtf(TAG, "Removed TIME_TICK alarm");
+ }
+ removedAlarms.add(removed);
+ }
+ }
+ return removedAlarms;
+ }
+
+ @Override
+ public void setAlarmClockRemovalListener(Runnable listener) {
+ mOnAlarmClockRemoved = listener;
+ }
+
+ @Override
+ public Alarm getNextWakeFromIdleAlarm() {
+ for (int i = mAlarms.size() - 1; i >= 0; i--) {
+ final Alarm alarm = mAlarms.get(i);
+ if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ return alarm;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return mAlarms.size();
+ }
+
+ @Override
+ public long getNextWakeupDeliveryTime() {
+ final long start = mStatLogger.getTime();
+ long nextWakeup = 0;
+ for (int i = mAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mAlarms.get(i);
+ if (!a.wakeup) {
+ continue;
+ }
+ if (nextWakeup == 0) {
+ nextWakeup = a.getMaxWhenElapsed();
+ } else {
+ if (a.getWhenElapsed() > nextWakeup) {
+ break;
+ }
+ nextWakeup = Math.min(nextWakeup, a.getMaxWhenElapsed());
+ }
+ }
+ mStatLogger.logDurationStat(Stats.GET_NEXT_WAKEUP_DELIVERY_TIME, start);
+ return nextWakeup;
+ }
+
+ @Override
+ public long getNextDeliveryTime() {
+ final long start = mStatLogger.getTime();
+ final int n = mAlarms.size();
+ if (n == 0) {
+ return 0;
+ }
+ long nextDelivery = mAlarms.get(n - 1).getMaxWhenElapsed();
+ for (int i = n - 2; i >= 0; i--) {
+ final Alarm a = mAlarms.get(i);
+ if (a.getWhenElapsed() > nextDelivery) {
+ break;
+ }
+ nextDelivery = Math.min(nextDelivery, a.getMaxWhenElapsed());
+ }
+ mStatLogger.logDurationStat(Stats.GET_NEXT_DELIVERY_TIME, start);
+ return nextDelivery;
+ }
+
+ @Override
+ public ArrayList<Alarm> removePendingAlarms(long nowElapsed) {
+ final ArrayList<Alarm> pending = new ArrayList<>();
+ final ArrayList<Alarm> standAlones = new ArrayList<>();
+
+ for (int i = mAlarms.size() - 1; i >= 0; i--) {
+ final Alarm alarm = mAlarms.get(i);
+ if (alarm.getWhenElapsed() > nowElapsed) {
+ break;
+ }
+ pending.add(alarm);
+ if ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) {
+ standAlones.add(alarm);
+ }
+ }
+ if (!standAlones.isEmpty()) {
+ // If there are deliverable standalone alarms, others must not go out yet.
+ mAlarms.removeAll(standAlones);
+ return standAlones;
+ }
+ mAlarms.removeAll(pending);
+ return pending;
+ }
+
+ @Override
+ public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
+ boolean changed = false;
+ for (final Alarm alarm : mAlarms) {
+ changed |= deliveryCalculator.updateAlarmDelivery(alarm);
+ }
+ if (changed) {
+ Collections.sort(mAlarms, sDecreasingTimeOrder);
+ }
+ return changed;
+ }
+
+ @Override
+ public ArrayList<Alarm> asList() {
+ final ArrayList<Alarm> copy = new ArrayList<>(mAlarms);
+ Collections.reverse(copy);
+ return copy;
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter ipw, long nowElapsed, SimpleDateFormat sdf) {
+ ipw.println(mAlarms.size() + " pending alarms: ");
+ ipw.increaseIndent();
+ dumpAlarmList(ipw, mAlarms, nowElapsed, sdf);
+ ipw.decreaseIndent();
+ mStatLogger.dump(ipw);
+ }
+
+ @Override
+ public void dumpProto(ProtoOutputStream pos, long nowElapsed) {
+ for (final Alarm a : mAlarms) {
+ a.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARMS, nowElapsed);
+ }
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index fbfce3c..93eb32c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5974,6 +5974,7 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isConversation();
method public boolean isDemoted();
method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
@@ -10682,8 +10683,6 @@
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
- field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
- field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12311,9 +12310,6 @@
field public static final int SYNCHRONOUS = 2; // 0x2
field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
- field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
- field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
- field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
@@ -16430,6 +16426,7 @@
method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
+ method public boolean isSameSource(@NonNull android.graphics.fonts.Font);
}
public static final class Font.Builder {
@@ -25277,6 +25274,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException;
+ ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int, @Nullable android.os.Handler, @Nullable android.media.MediaCas.EventListener) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method protected void finalize();
@@ -31408,6 +31406,7 @@
field @Deprecated public String FQDN;
field @Deprecated public static final int SECURITY_TYPE_EAP = 3; // 0x3
field @Deprecated public static final int SECURITY_TYPE_EAP_SUITE_B = 5; // 0x5
+ field @Deprecated public static final int SECURITY_TYPE_EAP_WPA3_ENTERPRISE = 9; // 0x9
field @Deprecated public static final int SECURITY_TYPE_OPEN = 0; // 0x0
field @Deprecated public static final int SECURITY_TYPE_OWE = 6; // 0x6
field @Deprecated public static final int SECURITY_TYPE_PSK = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 9cf0926..115b4d8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5090,6 +5090,7 @@
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+ field public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE = -1; // 0xffffffff
field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
field public static final int INVALID_LTS_ID = -1; // 0xffffffff
field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
@@ -5390,6 +5391,7 @@
public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getFirstMbInSlice();
method public int getMpuSequenceNumber();
method public long getPts();
method public int getScHevcIndexMask();
@@ -5430,9 +5432,14 @@
field public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = 128; // 0x80
field public static final int SC_HEVC_INDEX_SPS = 1; // 0x1
field public static final int SC_INDEX_B_FRAME = 4; // 0x4
+ field public static final int SC_INDEX_B_SLICE = 64; // 0x40
field public static final int SC_INDEX_I_FRAME = 1; // 0x1
+ field public static final int SC_INDEX_I_SLICE = 16; // 0x10
field public static final int SC_INDEX_P_FRAME = 2; // 0x2
+ field public static final int SC_INDEX_P_SLICE = 32; // 0x20
field public static final int SC_INDEX_SEQUENCE = 8; // 0x8
+ field public static final int SC_INDEX_SI_SLICE = 128; // 0x80
+ field public static final int SC_INDEX_SP_SLICE = 256; // 0x100
field public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = 4096; // 0x1000
field public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED = 8; // 0x8
field public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = 4; // 0x4
@@ -5553,6 +5560,7 @@
public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getFirstMbInSlice();
method public int getPacketId();
method public long getPts();
method public int getScIndexMask();
@@ -5878,6 +5886,7 @@
public class DvbsFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder builder();
method @Nullable public android.media.tv.tuner.frontend.DvbsCodeRate getCodeRate();
+ method public boolean getCouldHandleDiseqcRxMessage();
method public int getInputStreamId();
method public int getModulation();
method public int getPilot();
@@ -5887,7 +5896,6 @@
method public int getSymbolRate();
method public int getType();
method public int getVcmMode();
- method public boolean isDiseqcRxMessage();
field public static final int MODULATION_AUTO = 1; // 0x1
field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5931,7 +5939,7 @@
public static class DvbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
- method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
+ method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCouldHandleDiseqcRxMessage(boolean);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
@@ -6071,7 +6079,7 @@
}
public abstract class FrontendSettings {
- method public int getEndFrequency();
+ method @IntRange(from=1) public int getEndFrequency();
method public int getFrequency();
method public int getFrontendSpectralInversion();
method public abstract int getType();
@@ -6353,6 +6361,7 @@
public interface ScanCallback {
method public void onAnalogSifStandardReported(int);
method public void onAtsc3PlpInfosReported(@NonNull android.media.tv.tuner.frontend.Atsc3PlpInfo[]);
+ method public default void onDvbcAnnexReported(int);
method public void onDvbsStandardReported(int);
method public void onDvbtStandardReported(int);
method public void onFrequenciesReported(@NonNull int[]);
@@ -12337,6 +12346,17 @@
package android.telephony.ims {
+ public final class AudioCodecAttributes implements android.os.Parcelable {
+ ctor public AudioCodecAttributes(float, @NonNull android.util.Range<java.lang.Float>, float, @NonNull android.util.Range<java.lang.Float>);
+ method public int describeContents();
+ method public float getBandwidthKhz();
+ method @NonNull public android.util.Range<java.lang.Float> getBandwidthRangeKhz();
+ method public float getBitrateKbps();
+ method @NonNull public android.util.Range<java.lang.Float> getBitrateRangeKbps();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR;
+ }
+
public final class ImsCallForwardInfo implements android.os.Parcelable {
ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
method public int describeContents();
@@ -12705,6 +12725,7 @@
ctor public ImsStreamMediaProfile(int, int, int, int, int);
method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
method public int describeContents();
+ method @Nullable public android.telephony.ims.AudioCodecAttributes getAudioCodecAttributes();
method public int getAudioDirection();
method public int getAudioQuality();
method public int getRttMode();
@@ -12712,6 +12733,7 @@
method public int getVideoQuality();
method public boolean isReceivingRttAudio();
method public boolean isRttCall();
+ method public void setAudioCodecAttributes(@NonNull android.telephony.ims.AudioCodecAttributes);
method public void setReceivingRttAudio(boolean);
method public void setRttMode(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index 8683ca1..a0361d0 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -294,9 +294,9 @@
" -a|--authority <AUTHORITY>\n" +
" App-standby related options\n" +
"\n" +
- " -f|--foreground (cause WORKING_SET, FREQUENT sync adapters" +
- " to run immediately)\n" +
- " -F|--top (cause even RARE sync adapters to run immediately)\n" +
+ " -f|--foreground (defeat app-standby job throttling," +
+ " but not battery saver)\n" +
+ " -F|--top (defeat app-standby job throttling and battery saver)\n" +
" ContentResolver extra options:\n" +
" --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
" --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ac2a8e4..b270a52 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -502,6 +502,7 @@
MediametricsMediaParserReported mediametrics_mediaparser_reported = 316;
TlsHandshakeReported tls_handshake_reported = 317 [(module) = "conscrypt"];
TextClassifierApiUsageReported text_classifier_api_usage_reported = 318 [(module) = "textclassifier"];
+ KilledAppStatsReported killed_app_stats_reported = 319 [(module) = "carwatchdogd"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -4280,9 +4281,16 @@
optional android.stats.sysui.NotificationImportance old_importance = 5;
// New importance setting
optional android.stats.sysui.NotificationImportance importance = 6;
+ // whether or not this channel represents a conversation
+ optional bool is_conversation = 7;
+ // Hash of app-assigned notification conversation id
+ optional int32 conversation_id_hash = 8;
+ // whether or not the user demoted this channel out of the conversation space
+ optional bool is_conversation_demoted = 9;
+ // whether this conversation is marked as being a priority
+ optional bool is_conversation_priority = 10;
}
-
/**
* Logs when a biometric acquire event occurs.
*
@@ -5304,6 +5312,11 @@
LAUNCHER_APP_CLOSE_TO_HOME = 10;
LAUNCHER_APP_CLOSE_TO_PIP = 11;
LAUNCHER_QUICK_SWITCH = 12;
+ SHADE_HEADS_UP_APPEAR = 13;
+ SHADE_HEADS_UP_DISAPPEAR = 14;
+ SHADE_NOTIFICATION_ADD = 15;
+ SHADE_NOTIFICATION_REMOVE = 16;
+ SHADE_APP_LAUNCH = 17;
}
optional InteractionType interaction_type = 1;
@@ -12067,3 +12080,143 @@
optional ResultType result_type = 2;
optional int64 latency_millis = 3;
}
+
+/**
+ * Logs the current state of an application before it is killed.
+ *
+ * Pushed from:
+ * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp
+ */
+message KilledAppStatsReported {
+ // Linux process uid for the package.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Name of the package that was killed.
+ optional string package_name = 2;
+
+ // State of the application when it was killed.
+ enum AppState {
+ UNKNOWN_APP_STATE = 0;
+ BACKGROUND = 1;
+ FOREGROUND = 2;
+ }
+ optional AppState app_state = 3;
+
+ // System state indicating whether the system was in normal mode or garage mode.
+ enum SystemState {
+ UNKNOWN_SYSTEM_STATE = 0;
+ USER_INTERACTION_MODE = 1;
+ NO_USER_INTERACTION_MODE = 2;
+ }
+ optional SystemState system_state = 4;
+
+ // Reason for killing the application.
+ // Keep in sync between:
+ // packages/services/Car/watchdog/server/src/ApplicationTerminator.h
+ // frameworks/base/cmds/statsd/src/atoms.proto
+ enum KillReason {
+ UNKNOWN_KILL_REASON = 0;
+ KILLED_ON_ANR = 1;
+ KILLED_ON_IO_OVERUSE = 2;
+ KILLED_ON_MEMORY_OVERUSE = 3;
+ }
+ optional KillReason kill_reason = 5;
+
+ // Stats of the processes owned by the application when the application was killed.
+ // The process stack traces are not collected when the application was killed due to IO_OVERUSE.
+ optional ProcessStats process_stat = 6 [(log_mode) = MODE_BYTES];
+
+ // The application's I/O overuse stats logged only when the kill reason is KILLED_ON_IO_OVERUSE.
+ optional IoOveruseStats io_overuse_stats = 7 [(log_mode) = MODE_BYTES];
+}
+
+/**
+ * Logs I/O overuse stats for a package.
+ *
+ * Keep in sync between:
+ * packages/services/Car/watchdog/server/src/proto/statsd.proto
+ * frameworks/base/cmds/statsd/src/atoms.proto
+ *
+ * Logged from:
+ * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp
+ */
+message IoOveruseStats {
+ enum Period {
+ DAILY = 0;
+ WEEKLY = 1;
+ }
+
+ // Threshold and usage stats period.
+ optional Period period = 1;
+
+ // Threshold in-terms of write bytes defined for the package.
+ optional PerStateBytes threshold = 2;
+
+ // Number of write bytes in each state for the specified period.
+ optional PerStateBytes written_bytes = 3;
+};
+
+/**
+ * Logs bytes attributed to each application and system states.
+ *
+ * Keep in sync between:
+ * packages/services/Car/watchdog/server/src/proto/statsd.proto
+ * frameworks/base/cmds/statsd/src/atoms.proto
+ *
+ * Logged from:
+ * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp
+ */
+message PerStateBytes {
+ // Number of bytes attributed to the application foreground.
+ optional int64 foreground_bytes = 1;
+
+ // Number of bytes attributed to the application background.
+ optional int64 background_bytes = 2;
+
+ // Number of bytes attributed to the garage mode.
+ optional int64 garage_mode_bytes = 3;
+}
+
+/**
+ * Logs each ProcessStat in ProcessStats.
+ * Keep in sync between:
+ * packages/services/Car/watchdog/server/src/proto/statsd.proto
+ * frameworks/base/cmds/statsd/src/atoms.proto
+ * Logged from:
+ * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp
+ */
+message ProcessStats {
+ // Records the stats of the processes owned by an application.
+ repeated ProcessStat process_stat = 1;
+}
+
+/**
+ * Logs a process's stats.
+ * Keep in sync between:
+ * packages/services/Car/watchdog/server/src/proto/statsd.proto
+ * frameworks/base/cmds/statsd/src/atoms.proto
+ * Logged from:
+ * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp
+ */
+message ProcessStat {
+ // Command name of the process.
+ optional string process_name = 1;
+
+ // Process uptime.
+ optional uint64 uptime_milliseconds = 2;
+
+ // Number of major page faults caused by the process and its children.
+ optional uint64 major_page_faults = 3;
+
+ // Peak virtual memory size in kb.
+ optional uint64 vm_peak_kb = 4;
+
+ // Virtual memory size in kb.
+ optional uint64 vm_size_kb = 5;
+
+ // Peak resident set size (high water mark) in kb.
+ optional uint64 vm_hwm_kb = 6;
+
+ // Resident set size in kb.
+ optional uint64 vm_rss_kb = 7;
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index f85005b..9329e2a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5974,6 +5974,7 @@
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
+ method public boolean isConversation();
method public boolean isDemoted();
method public boolean isImportantConversation();
method public void setAllowBubbles(boolean);
@@ -10682,8 +10683,6 @@
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
- field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
- field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12311,9 +12310,6 @@
field public static final int SYNCHRONOUS = 2; // 0x2
field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
- field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
- field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
- field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
@@ -16412,6 +16408,7 @@
method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
+ method public boolean isSameSource(@NonNull android.graphics.fonts.Font);
}
public static final class Font.Builder {
@@ -25259,6 +25256,7 @@
public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException;
+ ctor public MediaCas(@NonNull android.content.Context, int, @Nullable String, int, @Nullable android.os.Handler, @Nullable android.media.MediaCas.EventListener) throws android.media.MediaCasException.UnsupportedCasException;
method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method protected void finalize();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b37f738..1e3a2c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5030,6 +5030,7 @@
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+ field public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE = -1; // 0xffffffff
field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
field public static final int INVALID_LTS_ID = -1; // 0xffffffff
field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
@@ -5330,6 +5331,7 @@
public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getFirstMbInSlice();
method public int getMpuSequenceNumber();
method public long getPts();
method public int getScHevcIndexMask();
@@ -5370,9 +5372,14 @@
field public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = 128; // 0x80
field public static final int SC_HEVC_INDEX_SPS = 1; // 0x1
field public static final int SC_INDEX_B_FRAME = 4; // 0x4
+ field public static final int SC_INDEX_B_SLICE = 64; // 0x40
field public static final int SC_INDEX_I_FRAME = 1; // 0x1
+ field public static final int SC_INDEX_I_SLICE = 16; // 0x10
field public static final int SC_INDEX_P_FRAME = 2; // 0x2
+ field public static final int SC_INDEX_P_SLICE = 32; // 0x20
field public static final int SC_INDEX_SEQUENCE = 8; // 0x8
+ field public static final int SC_INDEX_SI_SLICE = 128; // 0x80
+ field public static final int SC_INDEX_SP_SLICE = 256; // 0x100
field public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = 4096; // 0x1000
field public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED = 8; // 0x8
field public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = 4; // 0x4
@@ -5493,6 +5500,7 @@
public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getFirstMbInSlice();
method public int getPacketId();
method public long getPts();
method public int getScIndexMask();
@@ -5818,6 +5826,7 @@
public class DvbsFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder builder();
method @Nullable public android.media.tv.tuner.frontend.DvbsCodeRate getCodeRate();
+ method public boolean getCouldHandleDiseqcRxMessage();
method public int getInputStreamId();
method public int getModulation();
method public int getPilot();
@@ -5827,7 +5836,6 @@
method public int getSymbolRate();
method public int getType();
method public int getVcmMode();
- method public boolean isDiseqcRxMessage();
field public static final int MODULATION_AUTO = 1; // 0x1
field public static final int MODULATION_MOD_128APSK = 2048; // 0x800
field public static final int MODULATION_MOD_16APSK = 256; // 0x100
@@ -5871,7 +5879,7 @@
public static class DvbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
- method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean);
+ method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCouldHandleDiseqcRxMessage(boolean);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
@@ -6011,7 +6019,7 @@
}
public abstract class FrontendSettings {
- method public int getEndFrequency();
+ method @IntRange(from=1) public int getEndFrequency();
method public int getFrequency();
method public int getFrontendSpectralInversion();
method public abstract int getType();
@@ -6293,6 +6301,7 @@
public interface ScanCallback {
method public void onAnalogSifStandardReported(int);
method public void onAtsc3PlpInfosReported(@NonNull android.media.tv.tuner.frontend.Atsc3PlpInfo[]);
+ method public default void onDvbcAnnexReported(int);
method public void onDvbsStandardReported(int);
method public void onDvbtStandardReported(int);
method public void onFrequenciesReported(@NonNull int[]);
@@ -11179,6 +11188,17 @@
package android.telephony.ims {
+ public final class AudioCodecAttributes implements android.os.Parcelable {
+ ctor public AudioCodecAttributes(float, @NonNull android.util.Range<java.lang.Float>, float, @NonNull android.util.Range<java.lang.Float>);
+ method public int describeContents();
+ method public float getBandwidthKhz();
+ method @NonNull public android.util.Range<java.lang.Float> getBandwidthRangeKhz();
+ method public float getBitrateKbps();
+ method @NonNull public android.util.Range<java.lang.Float> getBitrateRangeKbps();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR;
+ }
+
public final class ImsCallForwardInfo implements android.os.Parcelable {
ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
method public int describeContents();
@@ -11547,6 +11567,7 @@
ctor public ImsStreamMediaProfile(int, int, int, int, int);
method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
method public int describeContents();
+ method @Nullable public android.telephony.ims.AudioCodecAttributes getAudioCodecAttributes();
method public int getAudioDirection();
method public int getAudioQuality();
method public int getRttMode();
@@ -11554,6 +11575,7 @@
method public int getVideoQuality();
method public boolean isReceivingRttAudio();
method public boolean isRttCall();
+ method public void setAudioCodecAttributes(@NonNull android.telephony.ims.AudioCodecAttributes);
method public void setReceivingRttAudio(boolean);
method public void setRttMode(int);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2ac345d..29e407b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -77,6 +77,7 @@
import android.os.Looper;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -8722,13 +8723,16 @@
* the activity is visible after the screen is turned on when the lockscreen is up. In addition,
* if this flag is set and the activity calls {@link
* KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
- * the screen will turn on.
+ * the screen will turn on. If the screen is off and device is not secured, this flag can turn
+ * screen on and dismiss keyguard to make this activity visible and resume, which can be used to
+ * replace {@link PowerManager#ACQUIRE_CAUSES_WAKEUP}
*
* @param turnScreenOn {@code true} to turn on the screen; {@code false} otherwise.
*
* @see #setShowWhenLocked(boolean)
* @see android.R.attr#turnScreenOn
* @see android.R.attr#showWhenLocked
+ * @see KeyguardManager#isDeviceSecure()
*/
public void setTurnScreenOn(boolean turnScreenOn) {
try {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b2a039f..ba57370 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
@@ -1741,11 +1743,6 @@
* @hide
*/
public void dump(PrintWriter pw, String indent) {
- final String activityType = WindowConfiguration.activityTypeToString(
- configuration.windowConfiguration.getActivityType());
- final String windowingMode = WindowConfiguration.activityTypeToString(
- configuration.windowConfiguration.getActivityType());
-
pw.println(); pw.print(" ");
pw.print(" id="); pw.print(persistentId);
pw.print(" stackId="); pw.print(stackId);
@@ -1771,8 +1768,8 @@
pw.print(" ");
pw.print(" isExcluded=");
pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
- pw.print(" activityType="); pw.print(activityType);
- pw.print(" windowingMode="); pw.print(windowingMode);
+ pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
+ pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
pw.print(" supportsSplitScreenMultiWindow=");
pw.println(supportsSplitScreenMultiWindow);
if (taskDescription != null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b609462..4eef616 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -286,7 +286,7 @@
return mSecurityViolation;
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 172409979)
public CompatibilityInfo getCompatibilityInfo() {
return mDisplayAdjustments.getCompatibilityInfo();
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index a06ffbd..080aac9 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -629,12 +629,20 @@
}
/**
+ * Whether or not this channel represents a conversation.
+ */
+ public boolean isConversation() {
+ return !TextUtils.isEmpty(getConversationId());
+ }
+
+
+ /**
* Whether or not notifications in this conversation are considered important.
*
* <p>Important conversations may get special visual treatment, and might be able to bypass DND.
*
- * <p>This is only valid for channels that represent conversations, that is, those with a valid
- * {@link #getConversationId() conversation id}.
+ * <p>This is only valid for channels that represent conversations, that is,
+ * where {@link #isConversation()} is true.
*/
public boolean isImportantConversation() {
return mImportantConvo;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 43b7722c..5dfab6e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -257,6 +257,18 @@
}
/** @hide */
+ @WindowConfiguration.WindowingMode
+ public int getWindowingMode() {
+ return configuration.windowConfiguration.getWindowingMode();
+ }
+
+ /** @hide */
+ @WindowConfiguration.ActivityType
+ public int getActivityType() {
+ return configuration.windowConfiguration.getActivityType();
+ }
+
+ /** @hide */
public void addLaunchCookie(IBinder cookie) {
if (cookie == null || launchCookies.contains(cookie)) return;
launchCookies.add(cookie);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 224e3a8..0ade5d2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -525,7 +525,7 @@
* @hide
*/
public static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
- "com.android.server.action.REMOTE_BUGREPORT_DISPATCH";
+ "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
/**
* Extra for shared bugreport's SHA-256 hash.
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 73becb1..e3395e2 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -74,8 +74,8 @@
/**
* The MIME type for a shortcut. The ClipData must include intents with required extras
- * {@link #EXTRA_PENDING_INTENT} and {@link Intent#EXTRA_USER}, and an optional
- * {@link #EXTRA_ACTIVITY_OPTIONS}.
+ * {@link Intent#EXTRA_SHORTCUT_ID}, {@link Intent#EXTRA_PACKAGE_NAME} and
+ * {@link Intent#EXTRA_USER}, and an optional {@link #EXTRA_ACTIVITY_OPTIONS}.
* @hide
*/
public static final String MIMETYPE_APPLICATION_SHORTCUT = "application/vnd.android.shortcut";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 782f0cd..a03bdf2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2740,6 +2740,7 @@
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
+ * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
@@ -2757,6 +2758,7 @@
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
+ * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_UNSTARTABLE =
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 946c634..a77d8b1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3824,18 +3824,21 @@
* Unstartable state with no root cause specified. E.g., data loader seeing missing pages but
* unclear about the cause. This corresponds to a generic alert window shown to the user when
* the user attempts to launch the app.
+ * @hide
*/
public static final int UNSTARTABLE_REASON_UNKNOWN = 0;
/**
* Unstartable state due to connection issues that interrupt package loading.
* This corresponds to an alert window shown to the user indicating connection errors.
+ * @hide
*/
public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1;
/**
* Unstartable state after encountering storage limitations.
* This corresponds to an alert window indicating limited storage.
+ * @hide
*/
public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 401bb9d..bf32560 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -42,9 +42,11 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
/**
* The {@link HdmiControlManager} class is used to send HDMI control messages
@@ -325,17 +327,17 @@
*
* @hide
*/
- public static final String HDMI_CEC_CONTROL_ENABLED = "1";
+ public static final int HDMI_CEC_CONTROL_ENABLED = 1;
/**
* HDMI CEC disabled.
*
* @hide
*/
- public static final String HDMI_CEC_CONTROL_DISABLED = "0";
+ public static final int HDMI_CEC_CONTROL_DISABLED = 0;
/**
* @hide
*/
- @StringDef({
+ @IntDef({
HDMI_CEC_CONTROL_ENABLED,
HDMI_CEC_CONTROL_DISABLED
})
@@ -401,17 +403,17 @@
*
* @hide
*/
- public static final String SYSTEM_AUDIO_MODE_MUTING_ENABLED = "1";
+ public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1;
/**
* System Audio Mode muting disabled.
*
* @hide
*/
- public static final String SYSTEM_AUDIO_MODE_MUTING_DISABLED = "0";
+ public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0;
/**
* @hide
*/
- @StringDef({
+ @IntDef({
SYSTEM_AUDIO_MODE_MUTING_ENABLED,
SYSTEM_AUDIO_MODE_MUTING_DISABLED
})
@@ -1317,24 +1319,51 @@
}
/**
- * Get a set of allowed values for a settings.
+ * Get a set of allowed values for a setting (string value-type).
*
* @param name name of the setting
* @return a set of allowed values for a settings. {@code null} on failure.
* @throws IllegalArgumentException when setting {@code name} does not exist.
+ * @throws IllegalArgumentException when setting {@code name} value type is invalid.
* @throws RuntimeException when the HdmiControlService is not available.
*
* @hide
*/
@NonNull
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public List<String> getAllowedCecSettingValues(@NonNull @CecSettingName String name) {
+ public List<String> getAllowedCecSettingStringValues(@NonNull @CecSettingName String name) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getAllowedCecSettingValues(name);
+ return mService.getAllowedCecSettingStringValues(name);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get a set of allowed values for a setting (int value-type).
+ *
+ * @param name name of the setting
+ * @return a set of allowed values for a settings. {@code null} on failure.
+ * @throws IllegalArgumentException when setting {@code name} does not exist.
+ * @throws IllegalArgumentException when setting {@code name} value type is invalid.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public List<Integer> getAllowedCecSettingIntValues(@NonNull @CecSettingName String name) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ int[] allowedValues = mService.getAllowedCecSettingIntValues(name);
+ return Arrays.stream(allowedValues).boxed().collect(Collectors.toList());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1350,13 +1379,13 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public void setHdmiCecEnabled(@NonNull @HdmiCecControl String value) {
+ public void setHdmiCecEnabled(@NonNull @HdmiCecControl int value) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- mService.setCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value);
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1373,13 +1402,13 @@
@NonNull
@HdmiCecControl
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public String getHdmiCecEnabled() {
+ public int getHdmiCecEnabled() {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1401,7 +1430,7 @@
throw new RuntimeException("HdmiControlService is not available");
}
try {
- mService.setCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+ mService.setCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1424,7 +1453,7 @@
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ return mService.getCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1447,7 +1476,7 @@
throw new RuntimeException("HdmiControlService is not available");
}
try {
- mService.setCecSettingValue(
+ mService.setCecSettingStringValue(
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1471,7 +1500,7 @@
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getCecSettingValue(
+ return mService.getCecSettingStringValue(
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1488,13 +1517,13 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting String value) {
+ public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting int value) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- mService.setCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1511,13 +1540,13 @@
@NonNull
@SystemAudioModeMuting
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public String getSystemAudioModeMuting() {
+ public int getSystemAudioModeMuting() {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 22d4640..fab56b8 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -301,18 +301,33 @@
}
@Override
- public List<String> getAllowedCecSettingValues(String name) {
- return HdmiControlServiceWrapper.this.getAllowedCecSettingValues(name);
+ public List<String> getAllowedCecSettingStringValues(String name) {
+ return HdmiControlServiceWrapper.this.getAllowedCecSettingStringValues(name);
}
@Override
- public String getCecSettingValue(String name) {
- return HdmiControlServiceWrapper.this.getCecSettingValue(name);
+ public int[] getAllowedCecSettingIntValues(String name) {
+ return HdmiControlServiceWrapper.this.getAllowedCecSettingIntValues(name);
}
@Override
- public void setCecSettingValue(String name, String value) {
- HdmiControlServiceWrapper.this.setCecSettingValue(name, value);
+ public String getCecSettingStringValue(String name) {
+ return HdmiControlServiceWrapper.this.getCecSettingStringValue(name);
+ }
+
+ @Override
+ public void setCecSettingStringValue(String name, String value) {
+ HdmiControlServiceWrapper.this.setCecSettingStringValue(name, value);
+ }
+
+ @Override
+ public int getCecSettingIntValue(String name) {
+ return HdmiControlServiceWrapper.this.getCecSettingIntValue(name);
+ }
+
+ @Override
+ public void setCecSettingIntValue(String name, int value) {
+ HdmiControlServiceWrapper.this.setCecSettingIntValue(name, value);
}
};
@@ -494,15 +509,30 @@
}
/** @hide */
- public List<String> getAllowedCecSettingValues(String name) {
+ public List<String> getAllowedCecSettingStringValues(String name) {
return new ArrayList<>();
}
/** @hide */
- public String getCecSettingValue(String name) {
+ public int[] getAllowedCecSettingIntValues(String name) {
+ return new int[0];
+ }
+
+ /** @hide */
+ public String getCecSettingStringValue(String name) {
return "";
}
/** @hide */
- public void setCecSettingValue(String name, String value) {}
+ public void setCecSettingStringValue(String name, String value) {
+ }
+
+ /** @hide */
+ public int getCecSettingIntValue(String name) {
+ return 0;
+ }
+
+ /** @hide */
+ public void setCecSettingIntValue(String name, int value) {
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6df164b..af9d3ac 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -88,7 +88,10 @@
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource();
List<String> getUserCecSettings();
- List<String> getAllowedCecSettingValues(String name);
- String getCecSettingValue(String name);
- void setCecSettingValue(String name, String value);
+ List<String> getAllowedCecSettingStringValues(String name);
+ int[] getAllowedCecSettingIntValues(String name);
+ String getCecSettingStringValue(String name);
+ void setCecSettingStringValue(String name, String value);
+ int getCecSettingIntValue(String name);
+ void setCecSettingIntValue(String name, int value);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 32a8c0a..44640c4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -46,6 +46,7 @@
import static android.inputmethodservice.InputMethodServiceProto.TOKEN;
import static android.inputmethodservice.InputMethodServiceProto.VIEWS_CREATED;
import static android.inputmethodservice.InputMethodServiceProto.WINDOW_VISIBLE;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowInsets.Type.navigationBars;
@@ -80,6 +81,7 @@
import android.os.IBinder;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.os.Trace;
import android.provider.Settings;
import android.text.InputType;
import android.text.Layout;
@@ -645,7 +647,9 @@
@Override
public void startInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.startInput");
doStartInput(ic, attribute, false);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/**
@@ -705,6 +709,8 @@
return;
}
final boolean wasVisible = isInputViewShown();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
+
applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
mShowInputFlags = 0;
mShowInputRequested = false;
@@ -717,6 +723,7 @@
: (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/**
@@ -748,6 +755,13 @@
+ " Use requestShowSelf(int) itself");
return;
}
+
+ if (Trace.isEnabled()) {
+ Binder.enableTracing();
+ } else {
+ Binder.disableTracing();
+ }
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
@@ -764,6 +778,7 @@
: (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/**
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index 77bfa57..f552aaa 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -17,7 +17,12 @@
package android.os;
import android.annotation.NonNull;
+import android.util.SparseArray;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -31,6 +36,8 @@
*/
public abstract class CombinedVibrationEffect implements Parcelable {
private static final int PARCEL_TOKEN_MONO = 1;
+ private static final int PARCEL_TOKEN_STEREO = 2;
+ private static final int PARCEL_TOKEN_SEQUENTIAL = 3;
/** @hide to prevent subclassing from outside of the framework */
public CombinedVibrationEffect() {
@@ -41,8 +48,8 @@
*
* A synced vibration effect should be performed by multiple vibrators at the same time.
*
- * @param effect The {@link VibrationEffect} to perform
- * @return The desired combined effect.
+ * @param effect The {@link VibrationEffect} to perform.
+ * @return The synced effect.
*/
@NonNull
public static CombinedVibrationEffect createSynced(@NonNull VibrationEffect effect) {
@@ -51,6 +58,30 @@
return combined;
}
+ /**
+ * Start creating a synced vibration effect.
+ *
+ * A synced vibration effect should be performed by multiple vibrators at the same time.
+ *
+ * @see CombinedVibrationEffect.SyncedCombination
+ */
+ @NonNull
+ public static SyncedCombination startSynced() {
+ return new SyncedCombination();
+ }
+
+ /**
+ * Start creating a sequential vibration effect.
+ *
+ * A sequential vibration effect should be performed by multiple vibrators in order.
+ *
+ * @see CombinedVibrationEffect.SequentialCombination
+ */
+ @NonNull
+ public static SequentialCombination startSequential() {
+ return new SequentialCombination();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -60,6 +91,164 @@
public abstract void validate();
/**
+ * A combination of haptic effects that should be played in multiple vibrators in sync.
+ *
+ * @hide
+ * @see CombinedVibrationEffect#startSynced()
+ */
+ public static final class SyncedCombination {
+
+ private final SparseArray<VibrationEffect> mEffects = new SparseArray<>();
+
+ SyncedCombination() {
+ }
+
+ /**
+ * Add or replace a one shot vibration effect to be performed by the specified vibrator.
+ *
+ * @param vibratorId The id of the vibrator that should perform this effect.
+ * @param effect The effect this vibrator should play.
+ * @return The {@link CombinedVibrationEffect.SyncedCombination} object to enable adding
+ * multiple effects in one chain.
+ * @see VibrationEffect#createOneShot(long, int)
+ */
+ @NonNull
+ public SyncedCombination addVibrator(int vibratorId, VibrationEffect effect) {
+ mEffects.put(vibratorId, effect);
+ return this;
+ }
+
+ /**
+ * Combine all of the added effects into a combined effect.
+ *
+ * The {@link CombinedVibrationEffect.SyncedCombination} object is still valid after this
+ * call, so you can continue adding more effects to it and generating more
+ * {@link CombinedVibrationEffect}s by calling this method again.
+ *
+ * @return The {@link CombinedVibrationEffect} resulting from combining the added effects to
+ * be played in sync.
+ */
+ @NonNull
+ public CombinedVibrationEffect combine() {
+ if (mEffects.size() == 0) {
+ throw new IllegalStateException(
+ "Combination must have at least one element to combine.");
+ }
+ CombinedVibrationEffect combined = new Stereo(mEffects);
+ combined.validate();
+ return combined;
+ }
+ }
+
+ /**
+ * A combination of haptic effects that should be played in multiple vibrators in sequence.
+ *
+ * @hide
+ * @see CombinedVibrationEffect#startSequential()
+ */
+ public static final class SequentialCombination {
+
+ private final ArrayList<CombinedVibrationEffect> mEffects = new ArrayList<>();
+ private final ArrayList<Integer> mDelays = new ArrayList<>();
+
+ SequentialCombination() {
+ }
+
+ /**
+ * Add a single vibration effect to be performed next.
+ *
+ * Similar to {@link #addNext(int, VibrationEffect, int)}, but with no delay.
+ *
+ * @param vibratorId The id of the vibrator that should perform this effect.
+ * @param effect The effect this vibrator should play.
+ * @return The {@link CombinedVibrationEffect.SequentialCombination} object to enable adding
+ * multiple effects in one chain.
+ */
+ @NonNull
+ public SequentialCombination addNext(int vibratorId, @NonNull VibrationEffect effect) {
+ return addNext(vibratorId, effect, /* delay= */ 0);
+ }
+
+ /**
+ * Add a single vibration effect to be performed next.
+ *
+ * @param vibratorId The id of the vibrator that should perform this effect.
+ * @param effect The effect this vibrator should play.
+ * @param delay The amount of time, in milliseconds, to wait between playing the prior
+ * effect and this one.
+ * @return The {@link CombinedVibrationEffect.SequentialCombination} object to enable adding
+ * multiple effects in one chain.
+ */
+ @NonNull
+ public SequentialCombination addNext(int vibratorId, @NonNull VibrationEffect effect,
+ int delay) {
+ return addNext(
+ CombinedVibrationEffect.startSynced().addVibrator(vibratorId, effect).combine(),
+ delay);
+ }
+
+ /**
+ * Add a combined vibration effect to be performed next.
+ *
+ * Similar to {@link #addNext(CombinedVibrationEffect, int)}, but with no delay.
+ *
+ * @param effect The combined effect to be performed next.
+ * @return The {@link CombinedVibrationEffect.SequentialCombination} object to enable adding
+ * multiple effects in one chain.
+ * @see VibrationEffect#createOneShot(long, int)
+ */
+ @NonNull
+ public SequentialCombination addNext(@NonNull CombinedVibrationEffect effect) {
+ return addNext(effect, /* delay= */ 0);
+ }
+
+ /**
+ * Add a one shot vibration effect to be performed by the specified vibrator.
+ *
+ * @param effect The combined effect to be performed next.
+ * @param delay The amount of time, in milliseconds, to wait between playing the prior
+ * effect and this one.
+ * @return The {@link CombinedVibrationEffect.SequentialCombination} object to enable adding
+ * multiple effects in one chain.
+ */
+ @NonNull
+ public SequentialCombination addNext(@NonNull CombinedVibrationEffect effect, int delay) {
+ if (effect instanceof Sequential) {
+ Sequential sequentialEffect = (Sequential) effect;
+ int firstEffectIndex = mDelays.size();
+ mEffects.addAll(sequentialEffect.getEffects());
+ mDelays.addAll(sequentialEffect.getDelays());
+ mDelays.set(firstEffectIndex, delay + mDelays.get(firstEffectIndex));
+ } else {
+ mEffects.add(effect);
+ mDelays.add(delay);
+ }
+ return this;
+ }
+
+ /**
+ * Combine all of the added effects in sequence.
+ *
+ * The {@link CombinedVibrationEffect.SequentialCombination} object is still valid after
+ * this call, so you can continue adding more effects to it and generating more {@link
+ * CombinedVibrationEffect}s by calling this method again.
+ *
+ * @return The {@link CombinedVibrationEffect} resulting from combining the added effects to
+ * be played in sequence.
+ */
+ @NonNull
+ public CombinedVibrationEffect combine() {
+ if (mEffects.size() == 0) {
+ throw new IllegalStateException(
+ "Combination must have at least one element to combine.");
+ }
+ CombinedVibrationEffect combined = new Sequential(mEffects, mDelays);
+ combined.validate();
+ return combined;
+ }
+ }
+
+ /**
* Represents a single {@link VibrationEffect} that should be executed in all vibrators in sync.
*
* @hide
@@ -87,10 +276,10 @@
@Override
public boolean equals(Object o) {
- if (!(o instanceof CombinedVibrationEffect.Mono)) {
+ if (!(o instanceof Mono)) {
return false;
}
- CombinedVibrationEffect.Mono other = (CombinedVibrationEffect.Mono) o;
+ Mono other = (Mono) o;
return other.mEffect.equals(other.mEffect);
}
@@ -128,6 +317,206 @@
};
}
+ /**
+ * Represents a list of {@link VibrationEffect}s that should be executed in sync.
+ *
+ * @hide
+ */
+ public static final class Stereo extends CombinedVibrationEffect {
+
+ /** Mapping vibrator ids to effects. */
+ private final SparseArray<VibrationEffect> mEffects;
+
+ public Stereo(Parcel in) {
+ int size = in.readInt();
+ mEffects = new SparseArray<>(size);
+ for (int i = 0; i < size; i++) {
+ int vibratorId = in.readInt();
+ mEffects.put(vibratorId, VibrationEffect.CREATOR.createFromParcel(in));
+ }
+ }
+
+ public Stereo(@NonNull SparseArray<VibrationEffect> effects) {
+ mEffects = new SparseArray<>(effects.size());
+ for (int i = 0; i < effects.size(); i++) {
+ mEffects.put(effects.keyAt(i), effects.valueAt(i));
+ }
+ }
+
+ /** Effects to be performed in sync, where each key represents the vibrator id. */
+ public SparseArray<VibrationEffect> getEffects() {
+ return mEffects;
+ }
+
+ /** @hide */
+ @Override
+ public void validate() {
+ Preconditions.checkArgument(mEffects.size() > 0,
+ "There should be at least one effect set for a combined effect");
+ for (int i = 0; i < mEffects.size(); i++) {
+ mEffects.valueAt(i).validate();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Stereo)) {
+ return false;
+ }
+ Stereo other = (Stereo) o;
+ if (mEffects.size() != other.mEffects.size()) {
+ return false;
+ }
+ for (int i = 0; i < mEffects.size(); i++) {
+ if (!mEffects.valueAt(i).equals(other.mEffects.get(mEffects.keyAt(i)))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEffects);
+ }
+
+ @Override
+ public String toString() {
+ return "Stereo{mEffects=" + mEffects + '}';
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_STEREO);
+ out.writeInt(mEffects.size());
+ for (int i = 0; i < mEffects.size(); i++) {
+ out.writeInt(mEffects.keyAt(i));
+ mEffects.valueAt(i).writeToParcel(out, flags);
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<Stereo> CREATOR =
+ new Parcelable.Creator<Stereo>() {
+ @Override
+ public Stereo createFromParcel(@NonNull Parcel in) {
+ // Skip the type token
+ in.readInt();
+ return new Stereo(in);
+ }
+
+ @Override
+ @NonNull
+ public Stereo[] newArray(int size) {
+ return new Stereo[size];
+ }
+ };
+ }
+
+ /**
+ * Represents a list of {@link VibrationEffect}s that should be executed in sequence.
+ *
+ * @hide
+ */
+ public static final class Sequential extends CombinedVibrationEffect {
+ private final List<CombinedVibrationEffect> mEffects;
+ private final List<Integer> mDelays;
+
+ public Sequential(Parcel in) {
+ int size = in.readInt();
+ mEffects = new ArrayList<>(size);
+ mDelays = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mDelays.add(in.readInt());
+ mEffects.add(CombinedVibrationEffect.CREATOR.createFromParcel(in));
+ }
+ }
+
+ public Sequential(@NonNull List<CombinedVibrationEffect> effects,
+ @NonNull List<Integer> delays) {
+ mEffects = new ArrayList<>(effects);
+ mDelays = new ArrayList<>(delays);
+ }
+
+ /** Effects to be performed in sequence. */
+ public List<CombinedVibrationEffect> getEffects() {
+ return mEffects;
+ }
+
+ /** Delay to be applied before each effect in {@link #getEffects()}. */
+ public List<Integer> getDelays() {
+ return mDelays;
+ }
+
+ /** @hide */
+ @Override
+ public void validate() {
+ Preconditions.checkArgument(mEffects.size() > 0,
+ "There should be at least one effect set for a combined effect");
+ Preconditions.checkArgument(mEffects.size() == mDelays.size(),
+ "Effect and delays should have equal length");
+ for (long delay : mDelays) {
+ if (delay < 0) {
+ throw new IllegalArgumentException("Delays must all be >= 0"
+ + " (delays=" + mDelays + ")");
+ }
+ }
+ for (CombinedVibrationEffect effect : mEffects) {
+ if (effect instanceof Sequential) {
+ throw new IllegalArgumentException(
+ "There should be no nested sequential effects in a combined effect");
+ }
+ effect.validate();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Sequential)) {
+ return false;
+ }
+ Sequential other = (Sequential) o;
+ return mDelays.equals(other.mDelays) && mEffects.equals(other.mEffects);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEffects);
+ }
+
+ @Override
+ public String toString() {
+ return "Sequential{mEffects=" + mEffects + ", mDelays=" + mDelays + '}';
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_SEQUENTIAL);
+ out.writeInt(mEffects.size());
+ for (int i = 0; i < mEffects.size(); i++) {
+ out.writeInt(mDelays.get(i));
+ mEffects.get(i).writeToParcel(out, flags);
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<Sequential> CREATOR =
+ new Parcelable.Creator<Sequential>() {
+ @Override
+ public Sequential createFromParcel(@NonNull Parcel in) {
+ // Skip the type token
+ in.readInt();
+ return new Sequential(in);
+ }
+
+ @Override
+ @NonNull
+ public Sequential[] newArray(int size) {
+ return new Sequential[size];
+ }
+ };
+ }
+
@NonNull
public static final Parcelable.Creator<CombinedVibrationEffect> CREATOR =
new Parcelable.Creator<CombinedVibrationEffect>() {
@@ -135,7 +524,11 @@
public CombinedVibrationEffect createFromParcel(Parcel in) {
int token = in.readInt();
if (token == PARCEL_TOKEN_MONO) {
- return new CombinedVibrationEffect.Mono(in);
+ return new Mono(in);
+ } else if (token == PARCEL_TOKEN_STEREO) {
+ return new Stereo(in);
+ } else if (token == PARCEL_TOKEN_SEQUENTIAL) {
+ return new Sequential(in);
} else {
throw new IllegalStateException(
"Unexpected combined vibration event type token in parcel.");
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2edd322..aba1f28 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4033,7 +4033,8 @@
* @return the {@link RemoveResult} code
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
try {
return mService.removeUserOrSetEphemeral(userId);
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index 2a809b1..4ffffc6 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -46,12 +46,13 @@
* CarrierMessagingService.
* @hide
*/
-public abstract class CarrierMessagingServiceWrapper {
+public final class CarrierMessagingServiceWrapper {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
private volatile ICarrierMessagingService mICarrierMessagingService;
+ private Runnable mOnServiceReadyCallback;
/**
* Binds to the carrier messaging service under package {@code carrierPackageName}. This method
@@ -63,12 +64,14 @@
* @hide
*/
public boolean bindToCarrierMessagingService(@NonNull Context context,
- @NonNull String carrierPackageName) {
+ @NonNull String carrierPackageName,
+ @NonNull Runnable onServiceReadyCallback) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
intent.setPackage(carrierPackageName);
mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
+ mOnServiceReadyCallback = onServiceReadyCallback;
return context.bindService(intent, mCarrierMessagingServiceConnection,
Context.BIND_AUTO_CREATE);
}
@@ -83,22 +86,17 @@
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
context.unbindService(mCarrierMessagingServiceConnection);
mCarrierMessagingServiceConnection = null;
+ mOnServiceReadyCallback = null;
}
/**
- * Implemented by subclasses to use the carrier messaging service once it is ready.
- * @hide
- */
- public abstract void onServiceReady();
-
- /**
* Called when connection with service is established.
*
* @param carrierMessagingService the carrier messaing service interface
*/
private void onServiceReady(ICarrierMessagingService carrierMessagingService) {
mICarrierMessagingService = carrierMessagingService;
- onServiceReady();
+ if (mOnServiceReadyCallback != null) mOnServiceReadyCallback.run();
}
/**
@@ -113,11 +111,11 @@
* @hide
*/
public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
- int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
+ int subId, @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.filterSms(pdu, format, destPort, subId,
- new CarrierMessagingCallbackWrapperInternal(callback));
+ new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -137,11 +135,11 @@
* @hide
*/
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
- int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
+ int sendSmsFlag, @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
- new CarrierMessagingCallbackWrapperInternal(callback));
+ new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -163,11 +161,11 @@
*/
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
int destPort, int sendSmsFlag,
- @NonNull final CarrierMessagingCallbackWrapper callback) {
+ @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
- sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+ sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -188,11 +186,11 @@
*/
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
@NonNull String destAddress, int sendSmsFlag,
- @NonNull final CarrierMessagingCallbackWrapper callback) {
+ @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
- sendSmsFlag, new CarrierMessagingCallbackWrapperInternal(callback));
+ sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -212,11 +210,11 @@
* @hide
*/
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
- @NonNull final CarrierMessagingCallbackWrapper callback) {
+ @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.sendMms(pduUri, subId, location,
- new CarrierMessagingCallbackWrapperInternal(callback));
+ new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -235,11 +233,11 @@
* @hide
*/
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
- @NonNull final CarrierMessagingCallbackWrapper callback) {
+ @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.downloadMms(pduUri, subId, location,
- new CarrierMessagingCallbackWrapperInternal(callback));
+ new CarrierMessagingCallbackInternal(callback));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -265,7 +263,7 @@
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
- public abstract static class CarrierMessagingCallbackWrapper {
+ public interface CarrierMessagingCallback {
/**
* Response callback for {@link CarrierMessagingServiceWrapper#filterSms}.
@@ -277,7 +275,7 @@
* {@see CarrierMessagingService#onReceiveTextSms}.
* @hide
*/
- public void onFilterComplete(int result) {
+ default void onFilterComplete(int result) {
}
@@ -291,7 +289,7 @@
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- public void onSendSmsComplete(int result, int messageRef) {
+ default void onSendSmsComplete(int result, int messageRef) {
}
@@ -305,7 +303,7 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
+ default void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
}
@@ -319,7 +317,7 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
+ default void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
}
@@ -330,43 +328,43 @@
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @hide
*/
- public void onDownloadMmsComplete(int result) {
+ default void onDownloadMmsComplete(int result) {
}
}
- private final class CarrierMessagingCallbackWrapperInternal
+ private final class CarrierMessagingCallbackInternal
extends ICarrierMessagingCallback.Stub {
- CarrierMessagingCallbackWrapper mCarrierMessagingCallbackWrapper;
+ CarrierMessagingCallback mCarrierMessagingCallback;
- CarrierMessagingCallbackWrapperInternal(CarrierMessagingCallbackWrapper callback) {
- mCarrierMessagingCallbackWrapper = callback;
+ CarrierMessagingCallbackInternal(CarrierMessagingCallback callback) {
+ mCarrierMessagingCallback = callback;
}
@Override
public void onFilterComplete(int result) throws RemoteException {
- mCarrierMessagingCallbackWrapper.onFilterComplete(result);
+ mCarrierMessagingCallback.onFilterComplete(result);
}
@Override
public void onSendSmsComplete(int result, int messageRef) throws RemoteException {
- mCarrierMessagingCallbackWrapper.onSendSmsComplete(result, messageRef);
+ mCarrierMessagingCallback.onSendSmsComplete(result, messageRef);
}
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs)
throws RemoteException {
- mCarrierMessagingCallbackWrapper.onSendMultipartSmsComplete(result, messageRefs);
+ mCarrierMessagingCallback.onSendMultipartSmsComplete(result, messageRefs);
}
@Override
public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException {
- mCarrierMessagingCallbackWrapper.onSendMmsComplete(result, sendConfPdu);
+ mCarrierMessagingCallback.onSendMmsComplete(result, sendConfPdu);
}
@Override
public void onDownloadMmsComplete(int result) throws RemoteException {
- mCarrierMessagingCallbackWrapper.onDownloadMmsComplete(result);
+ mCarrierMessagingCallback.onDownloadMmsComplete(result);
}
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index edcbbb3..4031ff8 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.i18n.timezone.TimeZoneDataFiles;
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
@@ -76,8 +77,7 @@
@NonNull
public static TzDataSetVersion read() throws IOException, TzDataSetException {
try {
- return new TzDataSetVersion(
- com.android.i18n.timezone.TzDataSetVersion.readTimeZoneModuleVersion());
+ return new TzDataSetVersion(TimeZoneDataFiles.readTimeZoneModuleVersion());
} catch (com.android.i18n.timezone.TzDataSetVersion.TzDataSetException e) {
throw new TzDataSetException(e.getMessage(), e);
}
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index cf32b92..33bc121 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -20,6 +20,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Angle measurement
*
@@ -75,6 +77,32 @@
return mConfidenceLevel;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof AngleMeasurement) {
+ AngleMeasurement other = (AngleMeasurement) obj;
+ return mRadians == other.getRadians()
+ && mErrorRadians == other.getErrorRadians()
+ && mConfidenceLevel == other.getConfidenceLevel();
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRadians, mErrorRadians, mConfidenceLevel);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 646bd42..cd5af69 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Represents an angle of arrival measurement between two devices using Ultra Wideband
*
@@ -72,6 +74,31 @@
return mAltitudeAngleMeasurement;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof AngleOfArrivalMeasurement) {
+ AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj;
+ return mAzimuthAngleMeasurement.equals(other.getAzimuth())
+ && mAltitudeAngleMeasurement.equals(other.getAltitude());
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAzimuthAngleMeasurement, mAltitudeAngleMeasurement);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index 9561be4..c959840 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -17,9 +17,12 @@
package android.uwb;
import android.annotation.FloatRange;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* A data point for the distance measurement
*
@@ -71,6 +74,32 @@
return mConfidenceLevel;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof DistanceMeasurement) {
+ DistanceMeasurement other = (DistanceMeasurement) obj;
+ return mMeters == other.getMeters()
+ && mErrorMeters == other.getErrorMeters()
+ && mConfidenceLevel == other.getConfidenceLevel();
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMeters, mErrorMeters, mConfidenceLevel);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
index d4d7fb2..f1c3162 100644
--- a/core/java/android/uwb/RangingMeasurement.java
+++ b/core/java/android/uwb/RangingMeasurement.java
@@ -26,6 +26,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Representation of a ranging measurement between the local device and a remote device
@@ -129,6 +130,35 @@
return mAngleOfArrivalMeasurement;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof RangingMeasurement) {
+ RangingMeasurement other = (RangingMeasurement) obj;
+ return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress())
+ && mStatus == other.getStatus()
+ && mElapsedRealtimeNanos == other.getElapsedRealtimeNanos()
+ && mDistanceMeasurement.equals(other.getDistance())
+ && mAngleOfArrivalMeasurement.equals(other.getAngleOfArrival());
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
+ mDistanceMeasurement, mAngleOfArrivalMeasurement);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java
index c5d4807..f23d9ed 100644
--- a/core/java/android/uwb/RangingParams.java
+++ b/core/java/android/uwb/RangingParams.java
@@ -30,6 +30,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -201,6 +202,43 @@
return new PersistableBundle(mSpecificationParameters);
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof RangingParams) {
+ RangingParams other = (RangingParams) obj;
+
+ return mIsInitiator == other.mIsInitiator
+ && mIsController == other.mIsController
+ && mSamplePeriod.equals(other.mSamplePeriod)
+ && mLocalDeviceAddress.equals(other.mLocalDeviceAddress)
+ && mRemoteDeviceAddresses.equals(other.mRemoteDeviceAddresses)
+ && mChannelNumber == other.mChannelNumber
+ && mTransmitPreambleCodeIndex == other.mTransmitPreambleCodeIndex
+ && mReceivePreambleCodeIndex == other.mReceivePreambleCodeIndex
+ && mStsPhyPacketType == other.mStsPhyPacketType
+ && mSpecificationParameters.size() == other.mSpecificationParameters.size()
+ && mSpecificationParameters.kindofEquals(other.mSpecificationParameters);
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIsInitiator, mIsController, mSamplePeriod, mLocalDeviceAddress,
+ mRemoteDeviceAddresses, mChannelNumber, mTransmitPreambleCodeIndex,
+ mReceivePreambleCodeIndex, mStsPhyPacketType, mSpecificationParameters);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
index 1d340b4..45180bf 100644
--- a/core/java/android/uwb/RangingReport.java
+++ b/core/java/android/uwb/RangingReport.java
@@ -17,11 +17,13 @@
package android.uwb;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* This class contains the UWB ranging data
@@ -50,6 +52,31 @@
return mRangingMeasurements;
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof RangingReport) {
+ RangingReport other = (RangingReport) obj;
+ return mRangingMeasurements.equals(other.getMeasurements());
+ }
+
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRangingMeasurements);
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/uwb/TEST_MAPPING b/core/java/android/uwb/TEST_MAPPING
new file mode 100644
index 0000000..9e50bd6
--- /dev/null
+++ b/core/java/android/uwb/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "UwbManagerTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 5780d4f..f4d5a7b 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
import static android.view.InsetsController.AnimationType;
@@ -24,6 +25,7 @@
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.InputMethodManager;
@@ -105,6 +107,7 @@
@Override
void notifyHidden() {
getImm().notifyImeHidden(mController.getHost().getWindowToken());
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
@Override
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index 108345e..4abffde 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.os.IBinder;
/**
@@ -31,17 +32,20 @@
private long ptr;
// Application name.
- public String name;
+ public final @NonNull String name;
// Dispatching timeout.
- public long dispatchingTimeoutMillis;
+ public final long dispatchingTimeoutMillis;
- public final IBinder token;
+ public final @NonNull IBinder token;
private native void nativeDispose();
- public InputApplicationHandle(IBinder token) {
+ public InputApplicationHandle(@NonNull IBinder token, @NonNull String name,
+ long dispatchingTimeoutMillis) {
this.token = token;
+ this.name = name;
+ this.dispatchingTimeoutMillis = dispatchingTimeoutMillis;
}
public InputApplicationHandle(InputApplicationHandle handle) {
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index d1a9a05..5a34a92 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -37,7 +37,7 @@
private long ptr;
// The input application handle.
- public final InputApplicationHandle inputApplicationHandle;
+ public InputApplicationHandle inputApplicationHandle;
// The token associates input data with a window and its input channel. The client input
// channel and the server input channel will both contain this token.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b5bf084..fbee833 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.InsetsControllerProto.CONTROL;
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
@@ -829,6 +830,11 @@
public void show(@InsetsType int types, boolean fromIme) {
if (fromIme) {
ImeTracing.getInstance().triggerDump();
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
+ } else {
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
}
// Handle pending request ready in case there was one set.
if (fromIme && mPendingImeControlRequest != null) {
@@ -880,6 +886,9 @@
void hide(@InsetsType int types, boolean fromIme) {
if (fromIme) {
ImeTracing.getInstance().triggerDump();
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
+ } else {
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
@@ -989,6 +998,7 @@
});
}
updateRequestedVisibility();
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
return;
}
@@ -1014,11 +1024,13 @@
cancellationSignal.setOnCancelListener(() -> {
cancelAnimation(runner, true /* invokeCallback */);
});
+ } else {
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
}
if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
- showDirectly(types);
+ showDirectly(types, fromIme);
} else {
- hideDirectly(types, false /* animationFinished */, animationType);
+ hideDirectly(types, false /* animationFinished */, animationType, fromIme);
}
updateRequestedVisibility();
}
@@ -1141,10 +1153,10 @@
cancelAnimation(runner, false /* invokeCallback */);
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
if (shown) {
- showDirectly(runner.getTypes());
+ showDirectly(runner.getTypes(), true /* fromIme */);
} else {
hideDirectly(runner.getTypes(), true /* animationFinished */,
- runner.getAnimationType());
+ runner.getAnimationType(), true /* fromIme */);
}
}
@@ -1314,11 +1326,11 @@
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
!hasAnimationCallbacks /* useInsetsAnimationThread */);
-
}
private void hideDirectly(
- @InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
+ @InsetsType int types, boolean animationFinished, @AnimationType int animationType,
+ boolean fromIme) {
if ((types & ime()) != 0) {
ImeTracing.getInstance().triggerDump();
}
@@ -1327,9 +1339,13 @@
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
}
updateRequestedVisibility();
+
+ if (fromIme) {
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
+ }
}
- private void showDirectly(@InsetsType int types) {
+ private void showDirectly(@InsetsType int types, boolean fromIme) {
if ((types & ime()) != 0) {
ImeTracing.getInstance().triggerDump();
}
@@ -1338,6 +1354,10 @@
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
}
updateRequestedVisibility();
+
+ if (fromIme) {
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
+ }
}
/**
@@ -1374,7 +1394,7 @@
if (WARN) Log.w(TAG, "startAnimation canceled before preDraw");
return;
}
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
+ Trace.asyncTraceBegin(TRACE_TAG_VIEW,
"InsetsAnimation: " + WindowInsets.Type.toString(types), types);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
@@ -1382,6 +1402,7 @@
runningAnimation.startDispatched = true;
}
}
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
mStartingAnimation = true;
controller.mReadyDispatched = true;
@@ -1392,7 +1413,7 @@
@VisibleForTesting
public void dispatchAnimationEnd(WindowInsetsAnimation animation) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
+ Trace.asyncTraceEnd(TRACE_TAG_VIEW,
"InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()),
animation.getTypeMask());
mHost.dispatchWindowInsetsAnimationEnd(animation);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e8cd54..9d24dff1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1890,8 +1890,10 @@
mSurface.release();
mSurfaceControl.release();
- // We should probably add an explicit dispose.
- mBlastBufferQueue = null;
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ mBlastBufferQueue = null;
+ }
}
/**
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 6ec093e..bc3e35c 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -88,9 +88,11 @@
public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1;
/**
- * Registers a DisplayAreaOrganizer to manage display areas for a given feature.
+ * Registers a DisplayAreaOrganizer to manage display areas for a given feature. A feature can
+ * not be registered by multiple organizers at the same time.
*
* @return a list of display areas that should be managed by the organizer.
+ * @throws IllegalStateException if the feature has already been registered.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@CallSuper
diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl
index 8943847..edabcf8 100644
--- a/core/java/android/window/IDisplayAreaOrganizerController.aidl
+++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl
@@ -24,9 +24,11 @@
interface IDisplayAreaOrganizerController {
/**
- * Registers a DisplayAreaOrganizer to manage display areas for a given feature.
+ * Registers a DisplayAreaOrganizer to manage display areas for a given feature. A feature can
+ * not be registered by multiple organizers at the same time.
*
* @return a list of display areas that should be managed by the organizer.
+ * @throws IllegalStateException if the feature has already been registered.
*/
ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(in IDisplayAreaOrganizer organizer,
int displayAreaFeature);
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index f46626b..ffc7f05 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -144,7 +144,7 @@
int HIDE_RECENTS_ANIMATION = 18;
/**
- * Hide soft input when {@link com.android.systemui.bubbles.BubbleController} is expanding,
+ * Hide soft input when {@link com.android.wm.shell.bubbles.BubbleController} is expanding,
* switching, or collapsing Bubbles.
*/
int HIDE_BUBBLES = 19;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index ba07863..137430b 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -22,7 +22,12 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
@@ -67,6 +72,11 @@
public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
+ public static final int CUJ_NOTIFICATION_HEADS_UP_APPEAR = 12;
+ public static final int CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR = 13;
+ public static final int CUJ_NOTIFICATION_ADD = 14;
+ public static final int CUJ_NOTIFICATION_REMOVE = 15;
+ public static final int CUJ_NOTIFICATION_APP_START = 16;
private static final int NO_STATSD_LOGGING = -1;
@@ -87,6 +97,11 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_REMOVE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH,
};
private static volatile InteractionJankMonitor sInstance;
@@ -112,6 +127,11 @@
CUJ_LAUNCHER_APP_CLOSE_TO_HOME,
CUJ_LAUNCHER_APP_CLOSE_TO_PIP,
CUJ_LAUNCHER_QUICK_SWITCH,
+ CUJ_NOTIFICATION_HEADS_UP_APPEAR,
+ CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
+ CUJ_NOTIFICATION_ADD,
+ CUJ_NOTIFICATION_REMOVE,
+ CUJ_NOTIFICATION_APP_START,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1123f20..dd1c87b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -204,6 +204,9 @@
],
shared_libs: [
+ "audioclient-types-aidl-unstable-cpp",
+ "audioflinger-aidl-unstable-cpp",
+ "av-types-aidl-unstable-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 7756a62..995bfa9 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -54,26 +54,28 @@
bool NativeInputApplicationHandle::updateInfo() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject obj = env->NewLocalRef(mObjWeak);
- if (!obj) {
+ ScopedLocalRef<jobject> obj(env, env->NewLocalRef(mObjWeak));
+ if (!obj.get()) {
return false;
}
+ if (mInfo.token.get() != nullptr) {
+ // The java fields are immutable, so it doesn't need to update again.
+ return true;
+ }
- mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
+ mInfo.name = getStringField(env, obj.get(), gInputApplicationHandleClassInfo.name, "<null>");
mInfo.dispatchingTimeoutMillis =
- env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);
+ env->GetLongField(obj.get(), gInputApplicationHandleClassInfo.dispatchingTimeoutMillis);
- jobject tokenObj = env->GetObjectField(obj,
- gInputApplicationHandleClassInfo.token);
- if (tokenObj) {
- mInfo.token = ibinderForJavaObject(env, tokenObj);
- env->DeleteLocalRef(tokenObj);
+ ScopedLocalRef<jobject> tokenObj(env, env->GetObjectField(obj.get(),
+ gInputApplicationHandleClassInfo.token));
+ if (tokenObj.get()) {
+ mInfo.token = ibinderForJavaObject(env, tokenObj.get());
} else {
mInfo.token.clear();
}
- env->DeleteLocalRef(obj);
return mInfo.token.get() != nullptr;
}
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index cbce38e..12d8bc6 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -162,7 +162,7 @@
static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jlong ptr) {
NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
- return map->getMap()->getKeyboardType();
+ return static_cast<jint>(map->getMap()->getKeyboardType());
}
static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
diff --git a/core/proto/android/server/alarm/alarmmanagerservice.proto b/core/proto/android/server/alarm/alarmmanagerservice.proto
index e1240245..8fe1bfc 100644
--- a/core/proto/android/server/alarm/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarm/alarmmanagerservice.proto
@@ -144,6 +144,8 @@
repeated IdleDispatchEntryProto allow_while_idle_dispatches = 40;
repeated WakeupEventProto recent_wakeup_history = 41;
+
+ repeated AlarmProto pending_alarms = 42;
}
// This is a soft wrapper for alarm clock information. It is not representative
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4df7d58..a778f14 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -349,7 +349,6 @@
<protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DEVICE_IDLE" />
- <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_DISPATCH" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" />
<protected-broadcast android:name="com.android.internal.action.EUICC_FACTORY_RESET" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6e03398..082397e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -817,6 +817,30 @@
<!-- B y-intercept --> <item>-0.198650895</item>
</string-array>
+ <string-array name="config_reduceBrightColorsCoefficientsNative">
+ <!-- R a-coefficient --> <item>-0.691218457</item>
+ <!-- R b-coefficient --> <item>0.050135153</item>
+ <!-- R y-intercept --> <item>0.917684143</item>
+ <!-- G a-coefficient --> <item>-0.691218457</item>
+ <!-- G b-coefficient --> <item>0.050135153</item>
+ <!-- G y-intercept --> <item>0.917684143</item>
+ <!-- B a-coefficient --> <item>-0.691218457</item>
+ <!-- B b-coefficient --> <item>0.050135153</item>
+ <!-- B y-intercept --> <item>0.917684143</item>
+ </string-array>
+
+ <string-array name="config_reduceBrightColorsCoefficients">
+ <!-- R a-coefficient --> <item>0.00000000000000154</item>
+ <!-- R b-coefficient --> <item>-1.0</item>
+ <!-- R y-intercept --> <item>1.045977011</item>
+ <!-- G a-coefficient --> <item>0.00000000000000224</item>
+ <!-- G b-coefficient --> <item>-1.0</item>
+ <!-- G y-intercept --> <item>1.045977011</item>
+ <!-- B a-coefficient --> <item>0.0000000000000022</item>
+ <!-- B b-coefficient --> <item>-1.0</item>
+ <!-- B y-intercept --> <item>1.045977011</item>
+ </string-array>
+
<!-- Boolean indicating whether display white balance is supported. -->
<bool name="config_displayWhiteBalanceAvailable">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a294c9d..4e4e3f8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3158,6 +3158,8 @@
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
+ <java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
+ <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
<java-symbol type="array" name="config_availableColorModes" />
<java-symbol type="integer" name="config_accessibilityColorMode" />
<java-symbol type="array" name="config_displayCompositionColorModes" />
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
index faa67a8..1947c6c 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -26,22 +26,135 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Arrays;
+
@Presubmit
@RunWith(JUnit4.class)
public class CombinedVibrationEffectTest {
+ private static final VibrationEffect VALID_EFFECT = VibrationEffect.createOneShot(10, 255);
+ private static final VibrationEffect INVALID_EFFECT = new VibrationEffect.OneShot(-1, -1);
+
@Test
public void testValidateMono() {
- CombinedVibrationEffect.createSynced(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ CombinedVibrationEffect.createSynced(VALID_EFFECT);
assertThrows(IllegalArgumentException.class,
- () -> CombinedVibrationEffect.createSynced(new VibrationEffect.OneShot(-1, -1)));
+ () -> CombinedVibrationEffect.createSynced(INVALID_EFFECT));
+ }
+
+ @Test
+ public void testValidateStereo() {
+ CombinedVibrationEffect.startSynced()
+ .addVibrator(0, VALID_EFFECT)
+ .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .combine();
+ CombinedVibrationEffect.startSynced()
+ .addVibrator(0, INVALID_EFFECT)
+ .addVibrator(0, VALID_EFFECT)
+ .combine();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> CombinedVibrationEffect.startSynced()
+ .addVibrator(0, INVALID_EFFECT)
+ .combine());
+ }
+
+ @Test
+ public void testValidateSequential() {
+ CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT)
+ .addNext(CombinedVibrationEffect.createSynced(VALID_EFFECT))
+ .combine();
+ CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT)
+ .addNext(0, VALID_EFFECT, 100)
+ .combine();
+ CombinedVibrationEffect.startSequential()
+ .addNext(CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT)
+ .combine())
+ .combine();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT, -1)
+ .combine());
+ assertThrows(IllegalArgumentException.class,
+ () -> CombinedVibrationEffect.startSequential()
+ .addNext(0, INVALID_EFFECT)
+ .combine());
+ assertThrows(IllegalArgumentException.class,
+ () -> new CombinedVibrationEffect.Sequential(
+ Arrays.asList(CombinedVibrationEffect.startSequential()
+ .addNext(CombinedVibrationEffect.createSynced(VALID_EFFECT))
+ .combine()),
+ Arrays.asList(0))
+ .validate());
+ }
+
+ @Test
+ public void testNestedSequentialAccumulatesDelays() {
+ CombinedVibrationEffect.Sequential combined =
+ (CombinedVibrationEffect.Sequential) CombinedVibrationEffect.startSequential()
+ .addNext(CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT, /* delay= */ 100)
+ .addNext(1, VALID_EFFECT, /* delay= */ 100)
+ .combine(),
+ /* delay= */ 10)
+ .addNext(CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT, /* delay= */ 100)
+ .combine())
+ .addNext(CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT)
+ .addNext(0, VALID_EFFECT, /* delay= */ 100)
+ .combine(),
+ /* delay= */ 10)
+ .combine();
+
+ assertEquals(Arrays.asList(110, 100, 100, 10, 100), combined.getDelays());
+ }
+
+ @Test
+ public void testCombineEmptyFails() {
+ assertThrows(IllegalStateException.class,
+ () -> CombinedVibrationEffect.startSynced().combine());
+ assertThrows(IllegalStateException.class,
+ () -> CombinedVibrationEffect.startSequential().combine());
}
@Test
public void testSerializationMono() {
- CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(VALID_EFFECT);
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
+ assertEquals(original, restored);
+ }
+
+ @Test
+ public void testSerializationStereo() {
+ CombinedVibrationEffect original = CombinedVibrationEffect.startSynced()
+ .addVibrator(0, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(1, VibrationEffect.createOneShot(10, 255))
+ .combine();
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
+ assertEquals(original, restored);
+ }
+
+ @Test
+ public void testSerializationSequential() {
+ CombinedVibrationEffect original = CombinedVibrationEffect.startSequential()
+ .addNext(0, VALID_EFFECT)
+ .addNext(CombinedVibrationEffect.createSynced(VALID_EFFECT))
+ .addNext(0, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), 100)
+ .combine();
Parcel parcel = Parcel.obtain();
original.writeToParcel(parcel, 0);
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index cc68bb6..4094f83 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -381,17 +381,31 @@
}
@Override
- public List<String> getAllowedCecSettingValues(String name) {
+ public List<String> getAllowedCecSettingStringValues(String name) {
return new ArrayList<>();
}
@Override
- public String getCecSettingValue(String name) {
+ public int[] getAllowedCecSettingIntValues(String name) {
+ return new int[0];
+ }
+
+ @Override
+ public String getCecSettingStringValue(String name) {
return "";
}
@Override
- public void setCecSettingValue(String name, String value) {
+ public void setCecSettingStringValue(String name, String value) {
+ }
+
+ @Override
+ public int getCecSettingIntValue(String name) {
+ return 0;
+ }
+
+ @Override
+ public void setCecSettingIntValue(String name, int value) {
}
}
diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp
new file mode 100644
index 0000000..c41c346
--- /dev/null
+++ b/core/tests/uwbtests/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 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.
+
+android_test {
+ name: "UwbManagerTests",
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/uwbtests/AndroidManifest.xml b/core/tests/uwbtests/AndroidManifest.xml
new file mode 100644
index 0000000..dc991ff
--- /dev/null
+++ b/core/tests/uwbtests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.uwb">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.uwb"
+ android:label="UWB Manager Tests">
+ </instrumentation>
+
+</manifest>
+
diff --git a/core/tests/uwbtests/AndroidTest.xml b/core/tests/uwbtests/AndroidTest.xml
new file mode 100644
index 0000000..ff4b668
--- /dev/null
+++ b/core/tests/uwbtests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<configuration description="Config for UWB Manager test cases">
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-suite-tag" value="apct-instrumentation"/>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="UwbManagerTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="UwbManagerTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.uwb" />
+ <option name="hidden-api-checks" value="false"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java
new file mode 100644
index 0000000..7769c28
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link AngleMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AngleMeasurementTest {
+ private static final double EPSILON = 0.00000000001;
+
+ @Test
+ public void testBuilder() {
+ double radians = 0.1234;
+ double errorRadians = 0.5678;
+ double confidence = 0.5;
+
+ AngleMeasurement.Builder builder = new AngleMeasurement.Builder();
+ tryBuild(builder, false);
+
+ builder.setRadians(radians);
+ tryBuild(builder, false);
+
+ builder.setErrorRadians(errorRadians);
+ tryBuild(builder, false);
+
+ builder.setConfidenceLevel(confidence);
+ AngleMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(measurement.getRadians(), radians, 0);
+ assertEquals(measurement.getErrorRadians(), errorRadians, 0);
+ assertEquals(measurement.getConfidenceLevel(), confidence, 0);
+ }
+
+ private AngleMeasurement tryBuild(AngleMeasurement.Builder builder, boolean expectSuccess) {
+ AngleMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected AngleMeasurement.Builder.build() to fail, but it succeeded");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected AngleMeasurement.Builder.build() to succeed, but it failed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ AngleMeasurement measurement = UwbTestUtils.getAngleMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AngleMeasurement fromParcel = AngleMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
new file mode 100644
index 0000000..077b08f
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link AngleOfArrivalMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AngleOfArrivalMeasurementTest {
+
+ @Test
+ public void testBuilder() {
+ AngleMeasurement azimuth = UwbTestUtils.getAngleMeasurement();
+ AngleMeasurement altitude = UwbTestUtils.getAngleMeasurement();
+
+ AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder();
+ tryBuild(builder, false);
+
+ builder.setAltitudeAngleMeasurement(altitude);
+ tryBuild(builder, false);
+
+ builder.setAzimuthAngleMeasurement(azimuth);
+ AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(azimuth, measurement.getAzimuth());
+ assertEquals(altitude, measurement.getAltitude());
+ }
+
+ private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) {
+ return new AngleMeasurement.Builder()
+ .setRadians(radian)
+ .setErrorRadians(error)
+ .setConfidenceLevel(confidence)
+ .build();
+ }
+
+ private AngleOfArrivalMeasurement tryBuild(AngleOfArrivalMeasurement.Builder builder,
+ boolean expectSuccess) {
+ AngleOfArrivalMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected AngleOfArrivalMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected AngleOfArrivalMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ AngleOfArrivalMeasurement measurement = UwbTestUtils.getAngleOfArrivalMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AngleOfArrivalMeasurement fromParcel =
+ AngleOfArrivalMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java
new file mode 100644
index 0000000..439c884
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link DistanceMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DistanceMeasurementTest {
+ private static final double EPSILON = 0.00000000001;
+
+ @Test
+ public void testBuilder() {
+ double meters = 0.12;
+ double error = 0.54;
+ double confidence = 0.99;
+
+ DistanceMeasurement.Builder builder = new DistanceMeasurement.Builder();
+ tryBuild(builder, false);
+
+ builder.setMeters(meters);
+ tryBuild(builder, false);
+
+ builder.setErrorMeters(error);
+ tryBuild(builder, false);
+
+ builder.setConfidenceLevel(confidence);
+ DistanceMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(meters, measurement.getMeters(), 0);
+ assertEquals(error, measurement.getErrorMeters(), 0);
+ assertEquals(confidence, measurement.getConfidenceLevel(), 0);
+ }
+
+ private DistanceMeasurement tryBuild(DistanceMeasurement.Builder builder,
+ boolean expectSuccess) {
+ DistanceMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ DistanceMeasurement measurement = UwbTestUtils.getDistanceMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ DistanceMeasurement fromParcel =
+ DistanceMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
new file mode 100644
index 0000000..a7559d8
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.os.SystemClock;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link RangingMeasurement}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingMeasurementTest {
+
+ @Test
+ public void testBuilder() {
+ int status = RangingMeasurement.RANGING_STATUS_SUCCESS;
+ UwbAddress address = UwbTestUtils.getUwbAddress(false);
+ long time = SystemClock.elapsedRealtimeNanos();
+ AngleOfArrivalMeasurement angleMeasurement = UwbTestUtils.getAngleOfArrivalMeasurement();
+ DistanceMeasurement distanceMeasurement = UwbTestUtils.getDistanceMeasurement();
+
+ RangingMeasurement.Builder builder = new RangingMeasurement.Builder();
+
+ builder.setStatus(status);
+ tryBuild(builder, false);
+
+ builder.setElapsedRealtimeNanos(time);
+ tryBuild(builder, false);
+
+ builder.setAngleOfArrivalMeasurement(angleMeasurement);
+ tryBuild(builder, false);
+
+ builder.setDistanceMeasurement(distanceMeasurement);
+ tryBuild(builder, false);
+
+ builder.setRemoteDeviceAddress(address);
+ RangingMeasurement measurement = tryBuild(builder, true);
+
+ assertEquals(status, measurement.getStatus());
+ assertEquals(address, measurement.getRemoteDeviceAddress());
+ assertEquals(time, measurement.getElapsedRealtimeNanos());
+ assertEquals(angleMeasurement, measurement.getAngleOfArrival());
+ assertEquals(distanceMeasurement, measurement.getDistance());
+ }
+
+ private RangingMeasurement tryBuild(RangingMeasurement.Builder builder,
+ boolean expectSuccess) {
+ RangingMeasurement measurement = null;
+ try {
+ measurement = builder.build();
+ if (!expectSuccess) {
+ fail("Expected RangingMeasurement.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected DistanceMeasurement.Builder.build() to succeed");
+ }
+ }
+ return measurement;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ RangingMeasurement measurement = UwbTestUtils.getRangingMeasurement();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RangingMeasurement fromParcel = RangingMeasurement.CREATOR.createFromParcel(parcel);
+ assertEquals(measurement, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java b/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java
new file mode 100644
index 0000000..8095c99
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/RangingParamsTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+
+/**
+ * Test of {@link RangingParams}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingParamsTest {
+
+ @Test
+ public void testParams_Build() {
+ UwbAddress local = UwbAddress.fromBytes(new byte[] {(byte) 0xA0, (byte) 0x57});
+ UwbAddress remote = UwbAddress.fromBytes(new byte[] {(byte) 0x4D, (byte) 0x8C});
+ int channel = 9;
+ int rxPreamble = 16;
+ int txPreamble = 21;
+ boolean isController = true;
+ boolean isInitiator = false;
+ @RangingParams.StsPhyPacketType int stsPhyType = RangingParams.STS_PHY_PACKET_TYPE_SP2;
+ Duration samplePeriod = Duration.ofSeconds(1, 234);
+ PersistableBundle specParams = new PersistableBundle();
+ specParams.putString("protocol", "some_protocol");
+
+ RangingParams params = new RangingParams.Builder()
+ .setChannelNumber(channel)
+ .setReceivePreambleCodeIndex(rxPreamble)
+ .setTransmitPreambleCodeIndex(txPreamble)
+ .setLocalDeviceAddress(local)
+ .addRemoteDeviceAddress(remote)
+ .setIsController(isController)
+ .setIsInitiator(isInitiator)
+ .setSamplePeriod(samplePeriod)
+ .setStsPhPacketType(stsPhyType)
+ .setSpecificationParameters(specParams)
+ .build();
+
+ assertEquals(params.getLocalDeviceAddress(), local);
+ assertEquals(params.getRemoteDeviceAddresses().size(), 1);
+ assertEquals(params.getRemoteDeviceAddresses().get(0), remote);
+ assertEquals(params.getChannelNumber(), channel);
+ assertEquals(params.isController(), isController);
+ assertEquals(params.isInitiator(), isInitiator);
+ assertEquals(params.getRxPreambleIndex(), rxPreamble);
+ assertEquals(params.getTxPreambleIndex(), txPreamble);
+ assertEquals(params.getStsPhyPacketType(), stsPhyType);
+ assertEquals(params.getSamplingPeriod(), samplePeriod);
+ assertTrue(params.getSpecificationParameters().kindofEquals(specParams));
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ RangingParams params = new RangingParams.Builder()
+ .setChannelNumber(9)
+ .setReceivePreambleCodeIndex(16)
+ .setTransmitPreambleCodeIndex(21)
+ .setLocalDeviceAddress(UwbTestUtils.getUwbAddress(false))
+ .addRemoteDeviceAddress(UwbTestUtils.getUwbAddress(true))
+ .setIsController(false)
+ .setIsInitiator(true)
+ .setSamplePeriod(Duration.ofSeconds(2))
+ .setStsPhPacketType(RangingParams.STS_PHY_PACKET_TYPE_SP1)
+ .build();
+ params.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RangingParams fromParcel = RangingParams.CREATOR.createFromParcel(parcel);
+ assertEquals(params, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingReportTest.java b/core/tests/uwbtests/src/android/uwb/RangingReportTest.java
new file mode 100644
index 0000000..64c48ba
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/RangingReportTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test of {@link RangingReport}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RangingReportTest {
+
+ @Test
+ public void testBuilder() {
+ List<RangingMeasurement> measurements = UwbTestUtils.getRangingMeasurements(5);
+
+ RangingReport.Builder builder = new RangingReport.Builder();
+ builder.addMeasurements(measurements);
+ RangingReport report = tryBuild(builder, true);
+ verifyMeasurementsEqual(measurements, report.getMeasurements());
+
+
+ builder = new RangingReport.Builder();
+ for (RangingMeasurement measurement : measurements) {
+ builder.addMeasurement(measurement);
+ }
+ report = tryBuild(builder, true);
+ verifyMeasurementsEqual(measurements, report.getMeasurements());
+ }
+
+ private void verifyMeasurementsEqual(List<RangingMeasurement> expected,
+ List<RangingMeasurement> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), actual.get(i));
+ }
+ }
+
+ private RangingReport tryBuild(RangingReport.Builder builder,
+ boolean expectSuccess) {
+ RangingReport report = null;
+ try {
+ report = builder.build();
+ if (!expectSuccess) {
+ fail("Expected RangingReport.Builder.build() to fail");
+ }
+ } catch (IllegalStateException e) {
+ if (expectSuccess) {
+ fail("Expected RangingReport.Builder.build() to succeed");
+ }
+ }
+ return report;
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ RangingReport report = UwbTestUtils.getRangingReports(5);
+ report.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RangingReport fromParcel = RangingReport.CREATOR.createFromParcel(parcel);
+ assertEquals(report, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java b/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java
new file mode 100644
index 0000000..ccc88a9
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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 android.uwb;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link UwbAddress}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UwbAddressTest {
+
+ @Test
+ public void testFromBytes_Short() {
+ runFromBytes(UwbAddress.SHORT_ADDRESS_BYTE_LENGTH);
+ }
+
+ @Test
+ public void testFromBytes_Extended() {
+ runFromBytes(UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH);
+ }
+
+ private void runFromBytes(int len) {
+ byte[] addressBytes = getByteArray(len);
+ UwbAddress address = UwbAddress.fromBytes(addressBytes);
+ assertEquals(address.size(), len);
+ assertEquals(addressBytes, address.toBytes());
+ }
+
+ private byte[] getByteArray(int len) {
+ byte[] res = new byte[len];
+ for (int i = 0; i < len; i++) {
+ res[i] = (byte) i;
+ }
+ return res;
+ }
+
+ @Test
+ public void testParcel_Short() {
+ runParcel(true);
+ }
+
+ @Test
+ public void testParcel_Extended() {
+ runParcel(false);
+ }
+
+ private void runParcel(boolean useShortAddress) {
+ Parcel parcel = Parcel.obtain();
+ UwbAddress address = UwbTestUtils.getUwbAddress(useShortAddress);
+ address.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ UwbAddress fromParcel = UwbAddress.CREATOR.createFromParcel(parcel);
+ assertEquals(address, fromParcel);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
new file mode 100644
index 0000000..62e0b62
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 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 android.uwb;
+
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UwbTestUtils {
+ private UwbTestUtils() {}
+
+ public static AngleMeasurement getAngleMeasurement() {
+ return new AngleMeasurement.Builder()
+ .setRadians(getDoubleInRange(-Math.PI, Math.PI))
+ .setErrorRadians(getDoubleInRange(0, Math.PI))
+ .setConfidenceLevel(getDoubleInRange(0, 1))
+ .build();
+ }
+
+ public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
+ return new AngleOfArrivalMeasurement.Builder()
+ .setAltitudeAngleMeasurement(getAngleMeasurement())
+ .setAzimuthAngleMeasurement(getAngleMeasurement())
+ .build();
+ }
+
+ public static DistanceMeasurement getDistanceMeasurement() {
+ return new DistanceMeasurement.Builder()
+ .setMeters(getDoubleInRange(0, 100))
+ .setErrorMeters(getDoubleInRange(0, 10))
+ .setConfidenceLevel(getDoubleInRange(0, 1))
+ .build();
+ }
+
+ public static RangingMeasurement getRangingMeasurement() {
+ return getRangingMeasurement(getUwbAddress(false));
+ }
+
+ public static RangingMeasurement getRangingMeasurement(UwbAddress address) {
+ return new RangingMeasurement.Builder()
+ .setDistanceMeasurement(getDistanceMeasurement())
+ .setAngleOfArrivalMeasurement(getAngleOfArrivalMeasurement())
+ .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos())
+ .setRemoteDeviceAddress(address != null ? address : getUwbAddress(false))
+ .setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
+ .build();
+ }
+
+ public static List<RangingMeasurement> getRangingMeasurements(int num) {
+ List<RangingMeasurement> result = new ArrayList<>();
+ for (int i = 0; i < num; i++) {
+ result.add(getRangingMeasurement());
+ }
+ return result;
+ }
+
+ public static RangingReport getRangingReports(int numMeasurements) {
+ RangingReport.Builder builder = new RangingReport.Builder();
+ for (int i = 0; i < numMeasurements; i++) {
+ builder.addMeasurement(getRangingMeasurement());
+ }
+ return builder.build();
+ }
+
+ private static double getDoubleInRange(double min, double max) {
+ return min + (max - min) * Math.random();
+ }
+
+ public static UwbAddress getUwbAddress(boolean isShortAddress) {
+ byte[] addressBytes = new byte[isShortAddress ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH :
+ UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH];
+ for (int i = 0; i < addressBytes.length; i++) {
+ addressBytes[i] = (byte) getDoubleInRange(1, 255);
+ }
+ return UwbAddress.fromBytes(addressBytes);
+ }
+}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index b143be7..441c163 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -327,7 +327,7 @@
* 1) Create Typeface from ttf file.
* <pre>
* <code>
- * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
+ * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf");
* Typeface typeface = builder.build();
* </code>
* </pre>
@@ -335,7 +335,7 @@
* 2) Create Typeface from ttc file in assets directory.
* <pre>
* <code>
- * Typeface.Builder buidler = new Typeface.Builder(getAssets(), "your_font_file.ttc");
+ * Typeface.Builder builder = new Typeface.Builder(getAssets(), "your_font_file.ttc");
* builder.setTtcIndex(2); // Set index of font collection.
* Typeface typeface = builder.build();
* </code>
@@ -344,7 +344,7 @@
* 3) Create Typeface with variation settings.
* <pre>
* <code>
- * Typeface.Builder buidler = new Typeface.Builder("your_font_file.ttf");
+ * Typeface.Builder builder = new Typeface.Builder("your_font_file.ttf");
* builder.setFontVariationSettings("'wght' 700, 'slnt' 20, 'ital' 1");
* builder.setWeight(700); // Tell the system that this is a bold font.
* builder.setItalic(true); // Tell the system that this is an italic style font.
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 586c512..c07b4bc 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -626,6 +626,51 @@
return mNativePtr;
}
+ /**
+ * Returns true if the given font is created from the same source data from this font.
+ *
+ * This method essentially compares {@link ByteBuffer} inside Font, but has some optimization
+ * for faster comparing. This method compares the internal object before going to one-by-one
+ * byte compare with {@link ByteBuffer}. This typically works efficiently if you compares the
+ * font that is created from {@link Builder#Builder(Font)}.
+ *
+ * This API is typically useful for checking if two fonts can be interpolated by font variation
+ * axes. For example, when you call {@link android.text.TextShaper} for the same
+ * string but different style, you may get two font objects which is created from the same
+ * source but have different parameters. You may want to animate between them by interpolating
+ * font variation settings if these fonts are created from the same source.
+ *
+ * @param other a font object to be compared.
+ * @return true if given font is created from the same source from this font. Otherwise false.
+ */
+ public boolean isSameSource(@NonNull Font other) {
+ Objects.requireNonNull(other);
+
+ // Shortcut for the same instance.
+ if (mBuffer == other.mBuffer) {
+ return true;
+ }
+
+ // Shortcut for different font buffer check by comparing size.
+ if (mBuffer.capacity() != other.mBuffer.capacity()) {
+ return false;
+ }
+
+ // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
+ // underlying native font object holds buffer address, check if this buffer points exactly
+ // the same address as a shortcut of equality. For being compatible with of API30 or before,
+ // check buffer position even if the buffer points the same address.
+ if (nIsSameBufferAddress(mNativePtr, other.mNativePtr)
+ && mBuffer.position() == other.mBuffer.position()) {
+ return true;
+ }
+
+ // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
+ // file but has the same file size, or two font has same content but they are allocated
+ // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
+ return mBuffer.equals(other.mBuffer);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
@@ -643,24 +688,7 @@
return false;
}
- // Shortcut for different font buffer check by comparing size.
- if (mBuffer.capacity() != f.mBuffer.capacity()) {
- return false;
- }
-
- // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
- // underlying native font object holds buffer address, check if this buffer points exactly
- // the same address as a shortcut of equality. For being compatible with of API30 or before,
- // check buffer position even if the buffer points the same address.
- if (nIsSameBufferAddress(mNativePtr, f.mNativePtr)
- && mBuffer.position() == f.mBuffer.position()) {
- return true;
- }
-
- // Unfortunately, need to compare bytes one-by-one since the buffer may be different font
- // file but has the same file size, or two font has same content but they are allocated
- // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
- return mBuffer.equals(f.mBuffer);
+ return isSameSource(f);
}
@Override
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 0defbd6..39e32c6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -116,12 +116,15 @@
"res",
],
static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.arch.core_core-runtime",
"androidx.dynamicanimation_dynamicanimation",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
+ "iconloader_base",
"protolog-lib",
+ "SettingsLib",
"WindowManager-Shell-proto",
- "androidx.appcompat_appcompat",
],
kotlincflags: ["-Xjvm-default=enable"],
manifest: "AndroidManifest.xml",
diff --git a/packages/SystemUI/res/layout/bubble_view.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
similarity index 62%
copy from packages/SystemUI/res/layout/bubble_view.xml
copy to libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
index 78f7cff..2104be4 100644
--- a/packages/SystemUI/res/layout/bubble_view.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2018 The Android Open Source Project
+ ~ 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.
@@ -12,10 +11,18 @@
~ 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
+ ~ limitations under the License.
-->
-<com.android.systemui.bubbles.BadgedImageView
+<!--
+ The transparent circle outline that encircles the bubbles when they're in the dismiss target.
+-->
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/bubble_view"
- android:layout_width="@dimen/individual_bubble_size"
- android:layout_height="@dimen/individual_bubble_size"/>
+ android:shape="oval">
+
+ <stroke
+ android:width="1dp"
+ android:color="#66FFFFFF" />
+
+ <solid android:color="#B3000000" />
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
new file mode 100644
index 0000000..ff8fede
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+<!-- The 'X' bubble dismiss icon. This is just ic_close with a stroke. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FFFFFFFF"
+ android:strokeColor="#FF000000"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_ic_create_bubble.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_create_bubble.xml
new file mode 100644
index 0000000..920671a2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/bubble_ic_create_bubble.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23,5v8h-2V5H3v14h10v2v0H3c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h18C22.1,3 23,3.9 23,5zM10,8v2.59L5.71,6.29L4.29,7.71L8.59,12H6v2h6V8H10zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_dark.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_empty_overflow_dark.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_empty_bubble_overflow_dark.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_ic_empty_overflow_dark.xml
diff --git a/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_light.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_empty_overflow_light.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_empty_bubble_overflow_light.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_ic_empty_overflow_light.xml
diff --git a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_overflow_button.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_ic_overflow_button.xml
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_ic_stop_bubble.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_stop_bubble.xml
new file mode 100644
index 0000000..8609576
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/bubble_ic_stop_bubble.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.29,14.71L7,10.41V13H5V7h6v2H8.41l4.29,4.29L11.29,14.71zM21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h10v0v-2H3V5h18v8h2V5C23,3.9 22.1,3 21,3zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_row.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_manage_menu_row.xml
index a793680..c61ac1c 100644
--- a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_row.xml
@@ -12,7 +12,7 @@
~ 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
+ ~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
diff --git a/packages/SystemUI/res/drawable/bubble_stack_user_education_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/bubble_stack_user_education_bg.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg.xml
diff --git a/packages/SystemUI/res/drawable/bubble_stack_user_education_bg_rtl.xml b/libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg_rtl.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/bubble_stack_user_education_bg_rtl.xml
rename to libs/WindowManager/Shell/res/drawable/bubble_stack_user_education_bg_rtl.xml
diff --git a/libs/WindowManager/Shell/res/drawable/ic_remove_no_shadow.xml b/libs/WindowManager/Shell/res/drawable/ic_remove_no_shadow.xml
new file mode 100644
index 0000000..265c501
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_remove_no_shadow.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorPrimary" >
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.41,12l5.29-5.29c0.39-0.39,0.39-1.02,0-1.41c-0.39-0.39-1.02-0.39-1.41,0L12,10.59L6.71,
+ 5.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41L10.59,12l-5.29,5.29c-0.39,0.39-0.39,1.02,
+ 0,1.41c0.39,0.39,1.02,0.39,1.41,0L12,13.41l5.29,5.29c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L13.41,12z"/>
+</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/rounded_bg_full.xml b/libs/WindowManager/Shell/res/drawable/rounded_bg_full.xml
new file mode 100644
index 0000000..e957445
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/rounded_bg_full.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius"
+ />
+</shape>
diff --git a/packages/SystemUI/res/layout/bubble_dismiss_target.xml b/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
similarity index 100%
rename from packages/SystemUI/res/layout/bubble_dismiss_target.xml
rename to libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml
similarity index 80%
rename from packages/SystemUI/res/layout/bubble_expanded_view.xml
rename to libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml
index db40c4f..54b08c6 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_expanded_view.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2018 The Android Open Source Project
+ ~ 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.
@@ -12,9 +12,9 @@
~ 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
+ ~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleExpandedView
+<com.android.wm.shell.bubbles.BubbleExpandedView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -27,7 +27,7 @@
android:layout_height="@dimen/bubble_pointer_height"
/>
- <com.android.systemui.statusbar.AlphaOptimizedButton
+ <com.android.wm.shell.common.AlphaOptimizedButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/settings_button"
android:layout_gravity="start"
@@ -35,7 +35,7 @@
android:layout_height="wrap_content"
android:focusable="true"
android:text="@string/manage_bubbles_text"
- android:textColor="?attr/wallpaperTextColor"
+ android:textColor="?android:attr/textColorPrimaryInverse"
/>
-</com.android.systemui.bubbles.BubbleExpandedView>
+</com.android.wm.shell.bubbles.BubbleExpandedView>
diff --git a/packages/SystemUI/res/layout/bubble_flyout.xml b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
similarity index 97%
rename from packages/SystemUI/res/layout/bubble_flyout.xml
rename to libs/WindowManager/Shell/res/layout/bubble_flyout.xml
index 22a8135..7fdf290 100644
--- a/packages/SystemUI/res/layout/bubble_flyout.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
@@ -34,7 +34,7 @@
android:layout_height="30dp"
android:layout_marginEnd="@dimen/bubble_flyout_avatar_message_space"
android:scaleType="centerInside"
- android:src="@drawable/ic_create_bubble"/>
+ android:src="@drawable/bubble_ic_create_bubble"/>
<LinearLayout
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
similarity index 95%
rename from packages/SystemUI/res/layout/bubble_manage_menu.xml
rename to libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 8494882..3a6aa80 100644
--- a/packages/SystemUI/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -35,7 +35,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_remove_no_shadow"
- android:tint="@color/global_actions_text"/>
+ android:tint="@color/bubbles_icon_tint"/>
<TextView
android:layout_width="wrap_content"
@@ -59,8 +59,8 @@
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
- android:src="@drawable/ic_stop_bubble"
- android:tint="@color/global_actions_text"/>
+ android:src="@drawable/bubble_ic_stop_bubble"
+ android:tint="@color/bubbles_icon_tint"/>
<TextView
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/bubble_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_menu_view.xml
similarity index 65%
rename from packages/SystemUI/res/layout/bubble_menu_view.xml
rename to libs/WindowManager/Shell/res/layout/bubble_menu_view.xml
index 24608d3..0c1d1a5 100644
--- a/packages/SystemUI/res/layout/bubble_menu_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_menu_view.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.bubbles.BubbleMenuView
+<com.android.wm.shell.bubbles.BubbleMenuView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -30,14 +30,14 @@
<ImageView
android:id="@*android:id/icon"
- android:layout_width="@dimen/global_actions_grid_item_icon_width"
- android:layout_height="@dimen/global_actions_grid_item_icon_height"
- android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
- android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
- android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
- android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
+ android:layout_width="@dimen/bubble_grid_item_icon_width"
+ android:layout_height="@dimen/bubble_grid_item_icon_height"
+ android:layout_marginTop="@dimen/bubble_grid_item_icon_top_margin"
+ android:layout_marginBottom="@dimen/bubble_grid_item_icon_bottom_margin"
+ android:layout_marginLeft="@dimen/bubble_grid_item_icon_side_margin"
+ android:layout_marginRight="@dimen/bubble_grid_item_icon_side_margin"
android:scaleType="centerInside"
- android:tint="@color/global_actions_text"
+ android:tint="@color/bubbles_icon_tint"
/>
</FrameLayout>
-</com.android.systemui.bubbles.BubbleMenuView>
+</com.android.wm.shell.bubbles.BubbleMenuView>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_activity.xml
similarity index 100%
rename from packages/SystemUI/res/layout/bubble_overflow_activity.xml
rename to libs/WindowManager/Shell/res/layout/bubble_overflow_activity.xml
diff --git a/packages/SystemUI/res/layout/bubble_overflow_button.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_button.xml
similarity index 89%
rename from packages/SystemUI/res/layout/bubble_overflow_button.xml
rename to libs/WindowManager/Shell/res/layout/bubble_overflow_button.xml
index 8f0fd4f..61000fe 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_button.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_button.xml
@@ -14,9 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.bubbles.BadgedImageView
+<com.android.wm.shell.bubbles.BadgedImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_overflow_button"
android:layout_width="@dimen/individual_bubble_size"
android:layout_height="@dimen/individual_bubble_size"
- android:src="@drawable/ic_bubble_overflow_button"/>
+ android:src="@drawable/bubble_ic_overflow_button"/>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
similarity index 96%
rename from packages/SystemUI/res/layout/bubble_overflow_view.xml
rename to libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
index 1218fba..c1f67bd 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <com.android.systemui.bubbles.BadgedImageView
+ <com.android.wm.shell.bubbles.BadgedImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_view"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
similarity index 100%
rename from packages/SystemUI/res/layout/bubble_stack_user_education.xml
rename to libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
diff --git a/packages/SystemUI/res/layout/bubble_view.xml b/libs/WindowManager/Shell/res/layout/bubble_view.xml
similarity index 94%
rename from packages/SystemUI/res/layout/bubble_view.xml
rename to libs/WindowManager/Shell/res/layout/bubble_view.xml
index 78f7cff..a28bd678 100644
--- a/packages/SystemUI/res/layout/bubble_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_view.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.bubbles.BadgedImageView
+<com.android.wm.shell.bubbles.BadgedImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_view"
android:layout_width="@dimen/individual_bubble_size"
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
similarity index 92%
rename from packages/SystemUI/res/layout/bubbles_manage_button_education.xml
rename to libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
index b51dc93..8de06c7 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
@@ -64,7 +64,7 @@
android:id="@+id/button_layout"
android:orientation="horizontal" >
- <com.android.systemui.statusbar.AlphaOptimizedButton
+ <com.android.wm.shell.common.AlphaOptimizedButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/manage"
android:layout_gravity="start"
@@ -73,10 +73,10 @@
android:focusable="true"
android:clickable="false"
android:text="@string/manage_bubbles_text"
- android:textColor="?attr/wallpaperTextColor"
+ android:textColor="?android:attr/textColorPrimaryInverse"
/>
- <com.android.systemui.statusbar.AlphaOptimizedButton
+ <com.android.wm.shell.common.AlphaOptimizedButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/got_it"
android:layout_gravity="start"
@@ -84,7 +84,7 @@
android:layout_height="wrap_content"
android:focusable="true"
android:text="@string/bubbles_user_education_got_it"
- android:textColor="?attr/wallpaperTextColor"
+ android:textColor="?android:attr/textColorPrimaryInverse"
/>
</LinearLayout>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index bee9f41..6342c00 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -79,12 +79,6 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
- "-712674749": {
- "message": "Clip description: %s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
"-710770147": {
"message": "Add target: %s",
"level": "VERBOSE",
@@ -121,6 +115,12 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java"
},
+ "375908576": {
+ "message": "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
"481673835": {
"message": "addListenerForTaskId taskId=%s",
"level": "VERBOSE",
@@ -169,12 +169,6 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java"
},
- "1842752748": {
- "message": "Clip description: handlingDrag=%b mimeTypes=%s",
- "level": "VERBOSE",
- "group": "WM_SHELL_DRAG_AND_DROP",
- "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
- },
"1862198614": {
"message": "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml
index 77a601d..aafba58 100644
--- a/libs/WindowManager/Shell/res/values-land/dimens.xml
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -18,4 +18,8 @@
<resources>
<dimen name="docked_divider_handle_width">2dp</dimen>
<dimen name="docked_divider_handle_height">16dp</dimen>
+
+ <!-- Padding between status bar and bubbles when displayed in expanded state, smaller
+ value in landscape since we have limited vertical space-->
+ <dimen name="bubble_padding_top">4dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml b/libs/WindowManager/Shell/res/values-night/colors.xml
similarity index 69%
copy from packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
copy to libs/WindowManager/Shell/res/values-night/colors.xml
index a793680..24b3640 100644
--- a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
+++ b/libs/WindowManager/Shell/res/values-night/colors.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 The Android Open Source Project
~
@@ -12,10 +11,10 @@
~ 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
+ ~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true">
- <ripple android:color="#99999999" />
- </item>
-</selector>
\ No newline at end of file
+
+<resources>
+ <!-- Bubbles -->
+ <color name="bubbles_icon_tint">@color/GM2_grey_200</color>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index cc3bf2a..1674d0b 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -25,4 +25,14 @@
<!-- Background for the various drop targets when handling drag and drop. -->
<color name="drop_outline_background">#330000FF</color>
+
+ <!-- Bubbles -->
+ <color name="bubbles_light">#FFFFFF</color>
+ <color name="bubbles_dark">@color/GM2_grey_800</color>
+ <color name="bubbles_icon_tint">@color/GM2_grey_700</color>
+
+ <!-- GM2 colors -->
+ <color name="GM2_grey_200">#E8EAED</color>
+ <color name="GM2_grey_700">#5F6368</color>
+ <color name="GM2_grey_800">#3C4043</color>
</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index b87a642..8a60aaf 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -75,4 +75,94 @@
<!-- The amount to inset the drop target regions from the edge of the display -->
<dimen name="drop_layout_display_margin">16dp</dimen>
+
+ <!-- The menu grid size for bubble menu. -->
+ <dimen name="bubble_grid_item_icon_width">20dp</dimen>
+ <dimen name="bubble_grid_item_icon_height">20dp</dimen>
+ <dimen name="bubble_grid_item_icon_top_margin">12dp</dimen>
+ <dimen name="bubble_grid_item_icon_bottom_margin">4dp</dimen>
+ <dimen name="bubble_grid_item_icon_side_margin">22dp</dimen>
+
+ <!-- How much each bubble is elevated. -->
+ <dimen name="bubble_elevation">1dp</dimen>
+ <!-- How much the bubble flyout text container is elevated. -->
+ <dimen name="bubble_flyout_elevation">4dp</dimen>
+ <!-- How much padding is around the left and right sides of the flyout text. -->
+ <dimen name="bubble_flyout_padding_x">12dp</dimen>
+ <!-- How much padding is around the top and bottom of the flyout text. -->
+ <dimen name="bubble_flyout_padding_y">10dp</dimen>
+ <!-- Size of the triangle that points from the flyout to the bubble stack. -->
+ <dimen name="bubble_flyout_pointer_size">6dp</dimen>
+ <!-- How much space to leave between the flyout (tip of the arrow) and the bubble stack. -->
+ <dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
+ <!-- How much space to leave between the flyout text and the avatar displayed in the flyout. -->
+ <dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
+ <!-- Padding between status bar and bubbles when displayed in expanded state -->
+ <dimen name="bubble_padding_top">16dp</dimen>
+ <!-- Size of individual bubbles. -->
+ <dimen name="individual_bubble_size">60dp</dimen>
+ <!-- Size of bubble bitmap. -->
+ <dimen name="bubble_bitmap_size">52dp</dimen>
+ <!-- Size of bubble icon bitmap. -->
+ <dimen name="bubble_overflow_icon_bitmap_size">24dp</dimen>
+ <!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
+ <dimen name="bubble_touch_padding">12dp</dimen>
+ <!-- Size of the circle around the bubbles when they're in the dismiss target. -->
+ <dimen name="bubble_dismiss_encircle_size">52dp</dimen>
+ <!-- Padding around the view displayed when the bubble is expanded -->
+ <dimen name="bubble_expanded_view_padding">4dp</dimen>
+ <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
+ a slight touch slop around the expanded view. -->
+ <dimen name="bubble_expanded_view_slop">8dp</dimen>
+ <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+ <dimen name="bubble_expanded_default_height">180dp</dimen>
+ <!-- Default height of bubble overflow -->
+ <dimen name="bubble_overflow_height">480dp</dimen>
+ <!-- Bubble overflow padding when there are no bubbles -->
+ <dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
+ <!-- Padding of container for overflow bubbles -->
+ <dimen name="bubble_overflow_padding">15dp</dimen>
+ <!-- Padding of label for bubble overflow view -->
+ <dimen name="bubble_overflow_text_padding">7dp</dimen>
+ <!-- Height of bubble overflow empty state illustration -->
+ <dimen name="bubble_empty_overflow_image_height">200dp</dimen>
+ <!-- Padding of bubble overflow empty state subtitle -->
+ <dimen name="bubble_empty_overflow_subtitle_padding">50dp</dimen>
+ <!-- Height of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_height">8dp</dimen>
+ <!-- Width of the triangle that points to the expanded bubble -->
+ <dimen name="bubble_pointer_width">12dp</dimen>
+ <!-- Extra padding around the dismiss target for bubbles -->
+ <dimen name="bubble_dismiss_slop">16dp</dimen>
+ <!-- Height of button allowing users to adjust settings for bubbles. -->
+ <dimen name="bubble_manage_button_height">48dp</dimen>
+ <!-- Max width of the message bubble-->
+ <dimen name="bubble_message_max_width">144dp</dimen>
+ <!-- Min width of the message bubble -->
+ <dimen name="bubble_message_min_width">32dp</dimen>
+ <!-- Interior padding of the message bubble -->
+ <dimen name="bubble_message_padding">4dp</dimen>
+ <!-- Offset between bubbles in their stacked position. -->
+ <dimen name="bubble_stack_offset">10dp</dimen>
+ <!-- Offset between stack y and animation y for bubble swap. -->
+ <dimen name="bubble_swap_animation_offset">15dp</dimen>
+ <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
+ <dimen name="bubble_stack_offscreen">9dp</dimen>
+ <!-- How far down the screen the stack starts. -->
+ <dimen name="bubble_stack_starting_offset_y">120dp</dimen>
+ <!-- Space between the pointer triangle and the bubble expanded view -->
+ <dimen name="bubble_pointer_margin">8dp</dimen>
+ <!-- Padding applied to the bubble dismiss target. Touches in this padding cause the bubbles to
+ snap to the dismiss target. -->
+ <dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
+ <dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
+ <dimen name="bubble_manage_menu_elevation">4dp</dimen>
+
+ <!-- Bubbles user education views -->
+ <dimen name="bubbles_manage_education_width">160dp</dimen>
+ <!-- The inset from the top bound of the manage button to place the user education. -->
+ <dimen name="bubbles_manage_education_top_inset">65dp</dimen>
+ <!-- Size of padding for the user education cling, this should at minimum be larger than
+ individual_bubble_size + some padding. -->
+ <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index fb89238..434a000 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -23,4 +23,21 @@
<item type="id" name="action_move_tl_50" />
<item type="id" name="action_move_tl_30" />
<item type="id" name="action_move_rb_full" />
+
+ <!-- For saving PhysicsAnimationLayout animations/animators as view tags. -->
+ <item type="id" name="translation_x_dynamicanimation_tag"/>
+ <item type="id" name="translation_y_dynamicanimation_tag"/>
+ <item type="id" name="translation_z_dynamicanimation_tag"/>
+ <item type="id" name="alpha_dynamicanimation_tag"/>
+ <item type="id" name="scale_x_dynamicanimation_tag"/>
+ <item type="id" name="scale_y_dynamicanimation_tag"/>
+ <item type="id" name="physics_animator_tag"/>
+ <item type="id" name="target_animator_tag" />
+ <item type="id" name="reorder_animator_tag"/>
+
+ <!-- Accessibility actions for bubbles. -->
+ <item type="id" name="action_move_top_left"/>
+ <item type="id" name="action_move_top_right"/>
+ <item type="id" name="action_move_bottom_left"/>
+ <item type="id" name="action_move_bottom_right"/>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/integers.xml b/libs/WindowManager/Shell/res/values/integers.xml
new file mode 100644
index 0000000..583bf33
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/integers.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<resources>
+ <!-- Maximum number of bubbles to render and animate at one time. While the animations used are
+ lightweight translation animations, this number can be reduced on lower end devices if any
+ performance issues arise. -->
+ <integer name="bubbles_max_rendered">5</integer>
+ <!-- Number of columns in bubble overflow. -->
+ <integer name="bubbles_overflow_columns">4</integer>
+ <!-- Maximum number of bubbles we allow in overflow before we dismiss the oldest one. -->
+ <integer name="bubbles_max_overflow">16</integer>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index da5965d..30ef72c 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -97,4 +97,53 @@
<string name="accessibility_action_start_one_handed">Start one-handed mode</string>
<!-- Accessibility description for stop one-handed mode [CHAR LIMIT=NONE] -->
<string name="accessibility_action_stop_one_handed">Exit one-handed mode</string>
+
+ <!-- Text used for content description of settings button in the header of expanded bubble
+ view. [CHAR_LIMIT=NONE] -->
+ <string name="bubbles_settings_button_description">Settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubbles</string>
+ <!-- Content description for button that shows bubble overflow on click [CHAR LIMIT=NONE] -->
+ <string name="bubble_overflow_button_content_description">Overflow</string>
+ <!-- Action to add overflow bubble back to stack. [CHAR LIMIT=NONE] -->
+ <string name="bubble_accessibility_action_add_back">Add back to stack</string>
+ <!-- Content description when a bubble is focused. [CHAR LIMIT=NONE] -->
+ <string name="bubble_content_description_single"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g></string>
+ <!-- Content description when the stack of bubbles is focused. [CHAR LIMIT=NONE] -->
+ <string name="bubble_content_description_stack"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g> and <xliff:g id="bubble_count" example="4">%3$d</xliff:g> more</string>
+ <!-- Action in accessibility menu to move the stack of bubbles to the top left of the screen. [CHAR LIMIT=30] -->
+ <string name="bubble_accessibility_action_move_top_left">Move top left</string>
+ <!-- Action in accessibility menu to move the stack of bubbles to the top right of the screen. [CHAR LIMIT=30] -->
+ <string name="bubble_accessibility_action_move_top_right">Move top right</string>
+ <!-- Action in accessibility menu to move the stack of bubbles to the bottom left of the screen. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
+ <!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
+ <string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
+ <!-- Label for the button that takes the user to the notification settings for the given app. -->
+ <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
+ <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
+ <string name="bubble_dismiss_text">Dismiss bubble</string>
+ <!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
+ <string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
+ <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
+ <string name="bubbles_user_education_title">Chat using bubbles</string>
+ <!-- Descriptive text for the bubble feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] -->
+ <string name="bubbles_user_education_description">New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it.</string>
+ <!-- Title text for the bubble "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]-->
+ <string name="bubbles_user_education_manage_title">Control bubbles anytime</string>
+ <!-- Descriptive text for the bubble "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]-->
+ <string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
+ <!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]-->
+ <string name="bubbles_user_education_got_it">Got it</string>
+ <!-- [CHAR LIMIT=NONE] Empty overflow title -->
+ <string name="bubble_overflow_empty_title">No recent bubbles</string>
+ <!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
+ <string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance title -->
+ <string name="notification_bubble_title">Bubble</string>
+
+ <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
+ <string name="manage_bubbles_text">Manage</string>
+
+ <!-- Content description to tell the user a bubble has been dismissed. -->
+ <string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 0a2cfbf..59a765d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -30,26 +30,23 @@
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Binder;
+import android.util.CloseGuard;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import com.android.wm.shell.ShellTaskOrganizer;
-
-import dalvik.system.CloseGuard;
-
import java.io.PrintWriter;
import java.util.concurrent.Executor;
/**
* View that can display a task.
*/
-// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
ShellTaskOrganizer.TaskListener {
+ /** Callback for listening task state. */
public interface Listener {
/** Called when the container is ready for launching activities. */
default void onInitialized() {}
@@ -70,7 +67,7 @@
default void onBackPressedOnTaskRoot(int taskId) {}
}
- private final CloseGuard mGuard = CloseGuard.get();
+ private final CloseGuard mGuard = new CloseGuard();
private final ShellTaskOrganizer mTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index a3b720c..8aca01d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -56,4 +56,10 @@
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
*/
public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+ /**
+ * Interpolator to be used when animating a panel closing.
+ */
+ public static final Interpolator PANEL_CLOSE_ACCELERATED =
+ new PathInterpolator(0.3f, 0, 0.5f, 1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 8bcffc8..4d06c03 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
+
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,14 +31,11 @@
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
/**
* View that displays an adaptive icon with an app-badge and a dot.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 09d9e03..93ed395 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.os.AsyncTask.Status.FINISHED;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 598a604..05acb55 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -22,8 +22,8 @@
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -59,8 +59,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.bubbles.dagger.BubbleModule;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -165,7 +163,7 @@
private boolean mIsStatusBarShade = true;
/**
- * Injected constructor. See {@link BubbleModule}.
+ * Injected constructor.
*/
public static BubbleController create(Context context,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
@@ -175,7 +173,7 @@
WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
- @Main Handler mainHandler,
+ Handler mainHandler,
ShellTaskOrganizer organizer) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
return new BubbleController(context,
@@ -903,7 +901,7 @@
}
if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
- && (!bubble.showInShade()
+ && (!bubble.showInShade()
|| reason == DISMISS_NOTIF_CANCEL
|| reason == DISMISS_GROUP_CANCELLED)) {
// The bubble is now gone & the notification is hidden from the shade, so
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 8cacc8f..b6a97e2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.app.PendingIntent;
@@ -33,8 +33,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles.DismissReason;
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.Bubbles.DismissReason;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 2ab9e87..fc565f1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles
+package com.android.wm.shell.bubbles
import android.annotation.SuppressLint
import android.annotation.UserIdInt
@@ -24,9 +24,9 @@
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
import android.os.UserHandle
import android.util.Log
-import com.android.systemui.bubbles.storage.BubbleEntity
-import com.android.systemui.bubbles.storage.BubblePersistentRepository
-import com.android.systemui.bubbles.storage.BubbleVolatileRepository
+import com.android.wm.shell.bubbles.storage.BubbleEntity
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
+import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 3937422..53f4e87 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.content.Context;
import android.provider.Settings;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
index a0d3391..ff68861 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEntry.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.app.Notification.FLAG_BUBBLE;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index ae3c683..74521c7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.systemui.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -54,10 +54,11 @@
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.systemui.R;
-import com.android.systemui.recents.TriangleShape;
-import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.wm.shell.R;
+import com.android.wm.shell.TaskView;
+import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.TriangleShape;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index d8b3250..460e0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-import static com.android.systemui.Interpolators.ALPHA_IN;
-import static com.android.systemui.Interpolators.ALPHA_OUT;
+
+import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -47,8 +48,8 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
-import com.android.systemui.recents.TriangleShape;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.TriangleShape;
/**
* Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index 371e849..2d9da21 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,14 +26,13 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.ShadowGenerator;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
/**
* Factory for creating normalized bubble icons.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index a24f5c2..3361c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 297144a..686d2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles
+package com.android.wm.shell.bubbles
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.Context
@@ -31,7 +31,7 @@
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
-import com.android.systemui.R
+import com.android.wm.shell.R
class BubbleOverflow(
private val context: Context,
@@ -72,7 +72,7 @@
updateResources()
expandedView.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
- overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+ overflowBtn.setImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java
index bc84173..2759b59 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowActivity.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_OVERFLOW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_OVERFLOW;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.Activity;
import android.content.Context;
@@ -43,13 +43,12 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
-
/**
* Activity for showing aged out bubbles.
* Must be public to be accessible to androidx...AppComponentFactory
@@ -168,8 +167,8 @@
final boolean isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES);
mEmptyStateImage.setImageDrawable(isNightMode
- ? res.getDrawable(R.drawable.ic_empty_bubble_overflow_dark)
- : res.getDrawable(R.drawable.ic_empty_bubble_overflow_light));
+ ? res.getDrawable(R.drawable.bubble_ic_empty_overflow_dark)
+ : res.getDrawable(R.drawable.bubble_ic_empty_overflow_light));
findViewById(android.R.id.content)
.setBackgroundColor(isNightMode
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 029caee..eccd009 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 69ed5b7..155f342 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -71,13 +71,13 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.animation.AnimatableScaleMatrix;
-import com.android.systemui.bubbles.animation.ExpandedAnimationController;
-import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
-import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
+import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
+import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
+import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -2661,14 +2661,17 @@
.floatValue();
}
+ /** Set the start position of the bubble stack. */
public void setStackStartPosition(RelativeStackPosition position) {
mStackAnimationController.setStackStartPosition(position);
}
+ /** @return the position of the bubble stack. */
public PointF getStackPosition() {
return mStackAnimationController.getStackPosition();
}
+ /** @return the relative position of the bubble stack. */
public RelativeStackPosition getRelativeStackPosition() {
return mStackAnimationController.getRelativeStackPosition();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index a3e6a1e..0b68306 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-import static com.android.systemui.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
+import static com.android.wm.shell.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.content.Context;
@@ -42,7 +42,7 @@
import com.android.internal.graphics.ColorUtils;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
import java.lang.ref.WeakReference;
import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 5890172..ec900be 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.graphics.Bitmap;
import android.graphics.Path;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 415edb1..79c42d8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index b3c552d..04b5ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -1,4 +1,20 @@
-package com.android.systemui.bubbles
+/*
+ * 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.bubbles
import android.content.Context
import android.graphics.drawable.TransitionDrawable
@@ -9,9 +25,9 @@
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
-import com.android.systemui.R
-import com.android.wm.shell.common.DismissCircleView
+import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
+import com.android.wm.shell.common.DismissCircleView
/*
* View that handles interactions between DismissCircleView and BubbleStackView.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 3db07c2..4cc6702 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles
+package com.android.wm.shell.bubbles
import android.content.Context
import android.graphics.Color
@@ -24,8 +24,8 @@
import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
-import com.android.systemui.Interpolators
-import com.android.systemui.R
+import com.android.wm.shell.R
+import com.android.wm.shell.animation.Interpolators
/**
* User education view to highlight the manage button that allows a user to configure the settings
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ObjectWrapper.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ObjectWrapper.java
index f054122..528907f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ObjectWrapper.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.os.Binder;
import android.os.IBinder;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
index b1291a5..b347329 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles
+package com.android.wm.shell.bubbles
import android.graphics.PointF
import android.os.Handler
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 216df2e..04c4dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles
+package com.android.wm.shell.bubbles
import android.content.Context
import android.graphics.Color
@@ -23,8 +23,8 @@
import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
-import com.android.systemui.Interpolators
-import com.android.systemui.R
+import com.android.wm.shell.R
+import com.android.wm.shell.animation.Interpolators
/**
* User education view to highlight the collapsed stack of bubbles.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
index 07acb71..2612b81 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/AnimatableScaleMatrix.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import android.graphics.Matrix;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 5a70401..61fbf81 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -28,10 +28,10 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.BubblePositioner;
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.google.android.collect.Sets;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/OneTimeEndListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OneTimeEndListener.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/bubbles/animation/OneTimeEndListener.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OneTimeEndListener.java
index 4e0abc8..37355c4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/OneTimeEndListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OneTimeEndListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import androidx.dynamicanimation.animation.DynamicAnimation;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
index 0a596d5..0618d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -35,7 +35,7 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 43893f2..d7f2e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import android.content.ContentResolver;
import android.content.res.Resources;
@@ -34,11 +34,11 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.BadgedImageView;
-import com.android.systemui.bubbles.BubblePositioner;
-import com.android.systemui.bubbles.BubbleStackView;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BadgedImageView;
+import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
index 24768cd..aeba302 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.annotation.DimenRes
import android.annotation.UserIdInt
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepository.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepository.kt
index ce0786d..66a75af 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepository.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.content.Context
import android.util.AtomicFile
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
index e0a7c78..7f0b165 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepository.kt
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.content.pm.LauncherApps
import android.os.UserHandle
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.bubbles.ShortcutKey
+import com.android.wm.shell.bubbles.ShortcutKey
private const val CAPACITY = 16
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
index bf163a2..fe72bd3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
-import com.android.internal.util.FastXmlSerializer
-import org.xmlpull.v1.XmlSerializer
-import java.io.IOException
import android.util.Xml
+import com.android.internal.util.FastXmlSerializer
import com.android.internal.util.XmlUtils
import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlSerializer
+import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.StandardCharsets
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AlphaOptimizedButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AlphaOptimizedButton.java
new file mode 100644
index 0000000..6f0a61b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AlphaOptimizedButton.java
@@ -0,0 +1,50 @@
+/*
+ * 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.common;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/**
+ * A Button which doesn't have overlapping drawing commands
+ *
+ * This is the copy from SystemUI/statusbar.
+ */
+public class AlphaOptimizedButton extends Button {
+ public AlphaOptimizedButton(Context context) {
+ super(context);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java
new file mode 100644
index 0000000..7079190
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java
@@ -0,0 +1,77 @@
+/*
+ * 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.common;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper around {@link PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ *
+ * This is the copy from SystemUI/recents.
+ */
+public class TriangleShape extends PathShape {
+ private Path mTriangularPath;
+
+ public TriangleShape(Path path, float stdWidth, float stdHeight) {
+ super(path, stdWidth, stdHeight);
+ mTriangularPath = path;
+ }
+
+ public static TriangleShape create(float width, float height, boolean isPointingUp) {
+ Path triangularPath = new Path();
+ if (isPointingUp) {
+ triangularPath.moveTo(0, height);
+ triangularPath.lineTo(width, height);
+ triangularPath.lineTo(width / 2, 0);
+ triangularPath.close();
+ } else {
+ triangularPath.moveTo(0, 0);
+ triangularPath.lineTo(width / 2, height);
+ triangularPath.lineTo(width, 0);
+ triangularPath.close();
+ }
+ return new TriangleShape(triangularPath, width, height);
+ }
+
+ /** Create an arrow TriangleShape that points to the left or the right */
+ public static TriangleShape createHorizontal(
+ float width, float height, boolean isPointingLeft) {
+ Path triangularPath = new Path();
+ if (isPointingLeft) {
+ triangularPath.moveTo(0, height / 2);
+ triangularPath.lineTo(width, height);
+ triangularPath.lineTo(width, 0);
+ triangularPath.close();
+ } else {
+ triangularPath.moveTo(0, height);
+ triangularPath.lineTo(width, height / 2);
+ triangularPath.lineTo(0, 0);
+ triangularPath.close();
+ }
+ return new TriangleShape(triangularPath, width, height);
+ }
+
+ @Override
+ public void getOutline(@NonNull Outline outline) {
+ outline.setPath(mTriangularPath);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index bf5b1d8..c77f594 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -16,17 +16,9 @@
package com.android.wm.shell.draganddrop;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
-import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_SHORTCUT_ID;
-import static android.content.Intent.EXTRA_TASK_ID;
-import static android.content.Intent.EXTRA_USER;
import static android.view.DragEvent.ACTION_DRAG_ENDED;
import static android.view.DragEvent.ACTION_DRAG_ENTERED;
import static android.view.DragEvent.ACTION_DRAG_EXITED;
@@ -42,25 +34,10 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.DragEvent;
import android.view.LayoutInflater;
@@ -76,6 +53,7 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreen;
+import java.util.Objects;
import java.util.Optional;
/**
@@ -91,10 +69,12 @@
private SplitScreen mSplitScreen;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
- private boolean mIsHandlingDrag;
- private DragLayout mDragLayout;
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ // A count of the number of active drags in progress to ensure that we only hide the window when
+ // all the drag animations have completed
+ private int mActiveDragCount;
+
public DragAndDropController(Context context, DisplayController displayController) {
mContext = context;
mDisplayController = displayController;
@@ -124,26 +104,31 @@
layoutParams.setFitInsetsTypes(0);
layoutParams.setTitle("ShellDropTarget");
- FrameLayout dropTarget = (FrameLayout) LayoutInflater.from(context).inflate(
+ FrameLayout rootView = (FrameLayout) LayoutInflater.from(context).inflate(
R.layout.global_drop_target, null);
- dropTarget.setOnDragListener(this);
- dropTarget.setVisibility(View.INVISIBLE);
- wm.addView(dropTarget, layoutParams);
- mDisplayDropTargets.put(displayId, new PerDisplay(displayId, context, wm, dropTarget));
+ rootView.setOnDragListener(this);
+ rootView.setVisibility(View.INVISIBLE);
+ DragLayout dragLayout = new DragLayout(context, mSplitScreen);
+ rootView.addView(dragLayout,
+ new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ wm.addView(rootView, layoutParams);
+
+ mDisplayDropTargets.put(displayId,
+ new PerDisplay(displayId, context, wm, rootView, dragLayout));
}
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display changed: %d", displayId);
final PerDisplay pd = mDisplayDropTargets.get(displayId);
- pd.dropTarget.requestApplyInsets();
+ pd.rootView.requestApplyInsets();
}
@Override
public void onDisplayRemoved(int displayId) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display removed: %d", displayId);
final PerDisplay pd = mDisplayDropTargets.get(displayId);
- pd.wm.removeViewImmediate(pd.dropTarget);
+ pd.wm.removeViewImmediate(pd.rootView);
mDisplayDropTargets.remove(displayId);
}
@@ -158,32 +143,33 @@
final ClipDescription description = event.getClipDescription();
if (event.getAction() == ACTION_DRAG_STARTED) {
- final boolean hasValidClipData = description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)
- || description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)
- || description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- mIsHandlingDrag = hasValidClipData;
+ final boolean hasValidClipData = event.getClipData().getItemCount() > 0
+ && (description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)
+ || description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)
+ || description.hasMimeType(MIMETYPE_APPLICATION_TASK));
+ pd.isHandlingDrag = hasValidClipData;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
- "Clip description: handlingDrag=%b mimeTypes=%s",
- mIsHandlingDrag, getMimeTypes(description));
+ "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
+ pd.isHandlingDrag, event.getClipData().getItemCount(),
+ getMimeTypes(description));
}
- if (!mIsHandlingDrag) {
+ if (!pd.isHandlingDrag) {
return false;
}
switch (event.getAction()) {
case ACTION_DRAG_STARTED:
- mDragLayout = new DragLayout(pd.context,
- mDisplayController.getDisplayLayout(displayId), mSplitScreen);
- pd.dropTarget.addView(mDragLayout,
- new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ mActiveDragCount++;
+ pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId),
+ event.getClipData());
setDropTargetWindowVisibility(pd, View.VISIBLE);
break;
case ACTION_DRAG_ENTERED:
- mDragLayout.show(event);
+ pd.dragLayout.show();
break;
case ACTION_DRAG_LOCATION:
- mDragLayout.update(event);
+ pd.dragLayout.update(event);
break;
case ACTION_DROP: {
return handleDrop(event, pd);
@@ -191,20 +177,22 @@
case ACTION_DRAG_EXITED: {
// Either one of DROP or EXITED will happen, and when EXITED we won't consume
// the drag surface
- mDragLayout.hide(event, null);
+ pd.dragLayout.hide(event, null);
break;
}
case ACTION_DRAG_ENDED:
// TODO(b/169894807): Ensure sure it's not possible to get ENDED without DROP
// or EXITED
- if (!mDragLayout.hasDropped()) {
- final View dragLayout = mDragLayout;
- mDragLayout.hide(event, () -> {
- setDropTargetWindowVisibility(pd, View.INVISIBLE);
- pd.dropTarget.removeView(dragLayout);
+ if (!pd.dragLayout.hasDropped()) {
+ mActiveDragCount--;
+ pd.dragLayout.hide(event, () -> {
+ if (mActiveDragCount == 0) {
+ // Hide the window if another drag hasn't been started while animating
+ // the drag-end
+ setDropTargetWindowVisibility(pd, View.INVISIBLE);
+ }
});
}
- mDragLayout = null;
break;
}
return true;
@@ -214,52 +202,14 @@
* Handles dropping on the drop target.
*/
private boolean handleDrop(DragEvent event, PerDisplay pd) {
- final ClipData data = event.getClipData();
- final ClipDescription description = event.getClipDescription();
final SurfaceControl dragSurface = event.getDragSurface();
- final View dragLayout = mDragLayout;
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- return mDragLayout.drop(event, dragSurface, (dropTargetBounds) -> {
- if (dropTargetBounds != null && data.getItemCount() > 0) {
- final Intent intent = data.getItemAt(0).getIntent();
- // TODO(b/169894807): Properly handle the drop, for now just launch it
- if (isTask) {
- int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- try {
- ActivityTaskManager.getService().startActivityFromRecents(
- taskId, null);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to launch task", e);
- }
- } else if (isShortcut) {
- try {
- Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
- : null;
- LauncherApps launcherApps =
- mContext.getSystemService(LauncherApps.class);
- launcherApps.startShortcut(
- intent.getStringExtra(EXTRA_PACKAGE_NAME),
- intent.getStringExtra(EXTRA_SHORTCUT_ID),
- null /* sourceBounds */, opts,
- intent.getParcelableExtra(EXTRA_USER));
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "Failed to launch shortcut", e);
- }
- } else {
- PendingIntent pi = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
- try {
- pi.send();
- } catch (PendingIntent.CanceledException e) {
- Slog.e(TAG, "Failed to launch activity", e);
- }
- }
+ mActiveDragCount--;
+ return pd.dragLayout.drop(event, dragSurface, () -> {
+ if (mActiveDragCount == 0) {
+ // Hide the window if another drag hasn't been started while animating the drop
+ setDropTargetWindowVisibility(pd, View.INVISIBLE);
}
- setDropTargetWindowVisibility(pd, View.INVISIBLE);
- pd.dropTarget.removeView(dragLayout);
-
// Clean up the drag surface
mTransaction.reparent(dragSurface, null);
mTransaction.apply();
@@ -270,9 +220,9 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Set drop target window visibility: displayId=%d visibility=%d",
pd.displayId, visibility);
- pd.dropTarget.setVisibility(visibility);
+ pd.rootView.setVisibility(visibility);
if (visibility == View.VISIBLE) {
- pd.dropTarget.requestApplyInsets();
+ pd.rootView.requestApplyInsets();
}
}
@@ -291,13 +241,17 @@
final int displayId;
final Context context;
final WindowManager wm;
- final FrameLayout dropTarget;
+ final FrameLayout rootView;
+ final DragLayout dragLayout;
- PerDisplay(int dispId, Context c, WindowManager w, FrameLayout l) {
+ boolean isHandlingDrag;
+
+ PerDisplay(int dispId, Context c, WindowManager w, FrameLayout rv, DragLayout dl) {
displayId = dispId;
context = c;
wm = w;
- dropTarget = l;
+ rootView = rv;
+ dragLayout = dl;
}
}
}
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
new file mode 100644
index 0000000..25890bc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -0,0 +1,409 @@
+/*
+ * 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.draganddrop;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
+import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_SHORTCUT_ID;
+import static android.content.Intent.EXTRA_TASK_ID;
+import static android.content.Intent.EXTRA_USER;
+
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.WindowConfiguration;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherApps;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The policy for handling drag and drop operations to shell.
+ */
+public class DragAndDropPolicy {
+
+ private static final String TAG = DragAndDropPolicy.class.getSimpleName();
+
+ private final Context mContext;
+ private final IActivityTaskManager mIActivityTaskManager;
+ private final Starter mStarter;
+ private final SplitScreen mSplitScreen;
+ private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
+
+ private DragSession mSession;
+
+ public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+ this(context, ActivityTaskManager.getService(), splitScreen,
+ new DefaultStarter(context, splitScreen));
+ }
+
+ @VisibleForTesting
+ DragAndDropPolicy(Context context, IActivityTaskManager activityTaskManager,
+ SplitScreen splitScreen, Starter starter) {
+ mContext = context;
+ mIActivityTaskManager = activityTaskManager;
+ mSplitScreen = splitScreen;
+ mStarter = starter;
+ }
+
+ /**
+ * Starts a new drag session with the given initial drag data.
+ */
+ void start(DisplayLayout displayLayout, ClipData data) {
+ mSession = new DragSession(mContext, mIActivityTaskManager, displayLayout, data);
+ // TODO(b/169894807): Also update the session data with task stack changes
+ mSession.update();
+ }
+
+ /**
+ * Returns the target's regions based on the current state of the device and display.
+ */
+ @NonNull
+ ArrayList<Target> getTargets(Insets insets) {
+ mTargets.clear();
+ if (mSession == null) {
+ // Return early if this isn't an app drag
+ return mTargets;
+ }
+
+ final int w = mSession.displayLayout.width();
+ final int h = mSession.displayLayout.height();
+ final int iw = w - insets.left - insets.right;
+ final int ih = h - insets.top - insets.bottom;
+ final int l = insets.left;
+ final int t = insets.top;
+ final boolean isVerticalSplit = mSession.isPhone && !mSession.displayLayout.isLandscape();
+ if (mSession.dragItemSupportsSplitscreen
+ && mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD
+ && mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN
+ && mSession.runningTaskIsResizeable) {
+ // Allow splitting when there is a fullscreen standard activity running
+ if (isVerticalSplit) {
+ // TODO(b/169894807): For now, only allow splitting to the right/bottom until we
+ // have split pairs
+ mTargets.add(new Target(TYPE_FULLSCREEN,
+ new Rect(0, 0, w, h / 2),
+ new Rect(l, t, l + iw, t + ih),
+ new Rect(0, 0, w, h)));
+ mTargets.add(new Target(TYPE_SPLIT_BOTTOM,
+ new Rect(0, h / 2, w, h),
+ new Rect(l, t + ih / 2, l + iw, t + ih),
+ new Rect(0, h / 2, w, h)));
+ } else {
+ mTargets.add(new Target(TYPE_FULLSCREEN,
+ new Rect(0, 0, w / 2, h),
+ new Rect(l, t, l + iw, t + ih),
+ new Rect(0, 0, w, h)));
+ mTargets.add(new Target(TYPE_SPLIT_RIGHT,
+ new Rect(w / 2, 0, w, h),
+ new Rect(l + iw / 2, t, l + iw, t + ih),
+ new Rect(w / 2, 0, w, h)));
+ }
+ } else if (mSession.dragItemSupportsSplitscreen
+ && mSplitScreen != null
+ && mSplitScreen.isDividerVisible()) {
+ // Already split, allow replacing existing split task
+ // TODO(b/169894807): For now, only allow replacing the non-primary task until we have
+ // split pairs
+ final Rect secondarySplitRawBounds =
+ mSplitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds();
+ final Rect secondarySplitBounds = new Rect(secondarySplitRawBounds);
+ secondarySplitBounds.intersect(new Rect(l, t, l + iw, t + ih));
+ if (isVerticalSplit) {
+ mTargets.add(new Target(TYPE_FULLSCREEN,
+ new Rect(0, 0, w, secondarySplitRawBounds.top),
+ new Rect(l, t, l + iw, t + ih),
+ new Rect(0, 0, w, secondarySplitRawBounds.top)));
+ } else {
+ mTargets.add(new Target(TYPE_FULLSCREEN,
+ new Rect(0, 0, secondarySplitRawBounds.left, h),
+ new Rect(l, t, l + iw, t + ih),
+ new Rect(0, 0, w, h)));
+ }
+ mTargets.add(new Target(isVerticalSplit ? TYPE_SPLIT_BOTTOM : TYPE_SPLIT_RIGHT,
+ new Rect(secondarySplitBounds),
+ new Rect(secondarySplitBounds),
+ new Rect(secondarySplitBounds)));
+ } else {
+ // Otherwise only show the fullscreen target
+ mTargets.add(new Target(TYPE_FULLSCREEN,
+ new Rect(0, 0, w, h),
+ new Rect(l, t, l + iw, t + ih),
+ new Rect(0, 0, w, h)));
+ }
+ return mTargets;
+ }
+
+ /**
+ * Returns the target at the given position based on the targets previously calculated.
+ */
+ @Nullable
+ Target getTargetAtLocation(int x, int y) {
+ for (int i = mTargets.size() - 1; i >= 0; i--) {
+ DragAndDropPolicy.Target t = mTargets.get(i);
+ if (t.hitRegion.contains(x, y)) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ void handleDrop(Target target, ClipData data) {
+ if (target == null || !mTargets.contains(target)) {
+ return;
+ }
+
+ final ClipDescription description = data.getDescription();
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Intent dragData = mSession.dragData;
+
+ if (target.type == TYPE_FULLSCREEN) {
+ if (mSplitScreen != null && mSplitScreen.isDividerVisible()) {
+ // If in split, remove split and launch fullscreen
+ mStarter.exitSplitScreen(mSession.runningTaskId);
+ } else {
+ // Not in split, fall through to launch
+ }
+ } else {
+ if (mSplitScreen != null && mSplitScreen.isDividerVisible()) {
+ // Split is already visible, just replace the task
+ // TODO(b/169894807): Since we only allow replacing the non-primary target above
+ // just fall through and start the activity
+ } else {
+ // Not in split, enter split now
+ mStarter.enterSplitScreen(mSession.runningTaskId,
+ target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP);
+ }
+ }
+
+ Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
+ : null;
+ if (isTask) {
+ mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
+ } else if (isShortcut) {
+ mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
+ dragData.getStringExtra(EXTRA_SHORTCUT_ID),
+ opts, dragData.getParcelableExtra(EXTRA_USER));
+ } else {
+ mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
+ }
+ }
+
+ /**
+ * Per-drag session data.
+ */
+ private static class DragSession {
+ private final Context mContext;
+ private final IActivityTaskManager mIActivityTaskManager;
+ private final ClipData mInitialDragData;
+
+ final DisplayLayout displayLayout;
+ Intent dragData;
+ int runningTaskId;
+ @WindowConfiguration.WindowingMode
+ int runningTaskWinMode = WINDOWING_MODE_UNDEFINED;
+ @WindowConfiguration.ActivityType
+ int runningTaskActType = ACTIVITY_TYPE_STANDARD;
+ boolean runningTaskIsResizeable;
+ boolean dragItemSupportsSplitscreen;
+ boolean isPhone;
+
+ DragSession(Context context, IActivityTaskManager activityTaskManager,
+ DisplayLayout dispLayout, ClipData data) {
+ mContext = context;
+ mIActivityTaskManager = activityTaskManager;
+ mInitialDragData = data;
+ displayLayout = dispLayout;
+ }
+
+ /**
+ * Updates the session data based on the current state of the system.
+ */
+ void update() {
+ final ClipDescription description = mInitialDragData.getDescription();
+ try {
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mIActivityTaskManager.getFilteredTasks(1,
+ false /* filterOnlyVisibleRecents */);
+ if (!tasks.isEmpty()) {
+ final ActivityManager.RunningTaskInfo task = tasks.get(0);
+ runningTaskWinMode = task.getWindowingMode();
+ runningTaskActType = task.getActivityType();
+ runningTaskId = task.taskId;
+ runningTaskIsResizeable = task.isResizeable;
+ }
+ } catch (RemoteException e) {
+ // Fall through
+ }
+
+ final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
+ dragItemSupportsSplitscreen = info == null
+ || ActivityInfo.isResizeableMode(info.resizeMode);
+ isPhone = mContext.getResources().getConfiguration().smallestScreenWidthDp < 600;
+ dragData = mInitialDragData.getItemAt(0).getIntent();
+ }
+ }
+
+ /**
+ * Interface for actually committing the task launches.
+ */
+ @VisibleForTesting
+ interface Starter {
+ void startTask(int taskId, Bundle activityOptions);
+ void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
+ UserHandle user);
+ void startIntent(PendingIntent intent, Bundle activityOptions);
+ void enterSplitScreen(int taskId, boolean leftOrTop);
+ void exitSplitScreen(int taskId);
+ }
+
+ /**
+ * Default implementation of the starter which calls through the system services to launch the
+ * tasks.
+ */
+ private static class DefaultStarter implements Starter {
+ private final Context mContext;
+ private final SplitScreen mSplitScreen;
+
+ public DefaultStarter(Context context, SplitScreen splitScreen) {
+ mContext = context;
+ mSplitScreen = splitScreen;
+ }
+
+ @Override
+ public void startTask(int taskId, Bundle activityOptions) {
+ try {
+ ActivityTaskManager.getService().startActivityFromRecents(taskId, null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
+ UserHandle user) {
+ try {
+ LauncherApps launcherApps =
+ mContext.getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+ activityOptions, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Failed to launch shortcut", e);
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, Bundle activityOptions) {
+ try {
+ intent.send(null, 0, null, null, null, null, activityOptions);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch activity", e);
+ }
+ }
+
+ @Override
+ public void enterSplitScreen(int taskId, boolean leftOrTop) {
+ mSplitScreen.splitPrimaryTask();
+ }
+
+ @Override
+ public void exitSplitScreen(int taskId) {
+ mSplitScreen.dismissSplitToPrimaryTask();
+ }
+ }
+
+ /**
+ * Represents a drop target.
+ */
+ static class Target {
+ static final int TYPE_FULLSCREEN = 0;
+ static final int TYPE_SPLIT_LEFT = 1;
+ static final int TYPE_SPLIT_TOP = 2;
+ static final int TYPE_SPLIT_RIGHT = 3;
+ static final int TYPE_SPLIT_BOTTOM = 4;
+ @IntDef(value = {
+ TYPE_FULLSCREEN,
+ TYPE_SPLIT_LEFT,
+ TYPE_SPLIT_TOP,
+ TYPE_SPLIT_RIGHT,
+ TYPE_SPLIT_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Type{}
+
+ final @Type int type;
+
+ // The actual hit region for this region
+ final Rect hitRegion;
+ // The approximate visual region for where the task will start
+ final Rect drawRegion;
+ // The
+ final Rect dropTargetBounds;
+
+ public Target(@Type int t, Rect hit, Rect draw, Rect drop) {
+ type = t;
+ hitRegion = hit;
+ drawRegion = draw;
+ dropTargetBounds = drop;
+ }
+
+ @Override
+ public String toString() {
+ return "Target {hit=" + hitRegion + " drop=" + dropTargetBounds + "}";
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index b70036b..fa857cdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -24,6 +24,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.content.ClipData;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Insets;
@@ -52,20 +53,19 @@
*/
public class DragLayout extends View {
- private final DisplayLayout mDisplayLayout;
- private final SplitScreen mSplitScreen;
+ private final DragAndDropPolicy mPolicy;
- private final ArrayList<Target> mTargets = new ArrayList<>();
- private Target mCurrentTarget = null;
+ private DragAndDropPolicy.Target mCurrentTarget = null;
private DropOutlineDrawable mDropOutline;
private int mDisplayMargin;
private Insets mInsets = Insets.NONE;
+
+ private boolean mIsShowing;
private boolean mHasDropped;
- public DragLayout(Context context, DisplayLayout displayLayout, SplitScreen splitscreen) {
+ public DragLayout(Context context, SplitScreen splitscreen) {
super(context);
- mDisplayLayout = displayLayout;
- mSplitScreen = splitscreen;
+ mPolicy = new DragAndDropPolicy(context, splitscreen);
mDisplayMargin = context.getResources().getDimensionPixelSize(
R.dimen.drop_layout_display_margin);
mDropOutline = new DropOutlineDrawable(context);
@@ -76,7 +76,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mInsets = insets.getInsets(Type.systemBars() | Type.displayCutout());
- calculateDropTargets();
+ recomputeDropTargets();
return super.onApplyWindowInsets(insets);
}
@@ -100,66 +100,41 @@
return mHasDropped;
}
- public void show(DragEvent event) {
- calculateDropTargets();
+ public void prepare(DisplayLayout displayLayout, ClipData initialData) {
+ mPolicy.start(displayLayout, initialData);
mHasDropped = false;
+ mCurrentTarget = null;
}
- private void calculateDropTargets() {
- // Calculate all the regions based on split and landscape portrait
- // TODO: Filter based on clip data
- final float SIDE_MARGIN_PCT = 0.3f;
- final int w = mDisplayLayout.width();
- final int h = mDisplayLayout.height();
- final int iw = w - mInsets.left - mInsets.right;
- final int ih = h - mInsets.top - mInsets.bottom;
- final int l = mInsets.left;
- final int t = mInsets.top;
- final int r = mInsets.right;
- final int b = mInsets.bottom;
- mTargets.clear();
-
- // Left split
- addTarget(new Target(
- new Rect(0, 0,
- (int) (w * SIDE_MARGIN_PCT), h),
- new Rect(l + mDisplayMargin, t + mDisplayMargin,
- l + iw / 2 - mDisplayMargin, t + ih - mDisplayMargin),
- new Rect(0, 0, w / 2, h)));
-
- // Fullscreen
- addTarget(new Target(
- new Rect((int) (w * SIDE_MARGIN_PCT), 0,
- w - (int) (w * SIDE_MARGIN_PCT), h),
- new Rect(l + mDisplayMargin, t + mDisplayMargin,
- l + iw - mDisplayMargin, t + ih - mDisplayMargin),
- new Rect(0, 0, w, h)));
-
- // Right split
- addTarget(new Target(
- new Rect(w - (int) (w * SIDE_MARGIN_PCT), 0,
- w, h),
- new Rect(l + iw / 2 + mDisplayMargin, t + mDisplayMargin,
- l + iw - mDisplayMargin, t + ih - mDisplayMargin),
- new Rect(w / 2, 0, w, h)));
+ public void show() {
+ mIsShowing = true;
+ recomputeDropTargets();
}
- private void addTarget(Target t) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Add target: %s", t);
- mTargets.add(t);
+ /**
+ * Recalculates the drop targets based on the current policy.
+ */
+ private void recomputeDropTargets() {
+ if (!mIsShowing) {
+ return;
+ }
+ final ArrayList<DragAndDropPolicy.Target> targets = mPolicy.getTargets(mInsets);
+ for (int i = 0; i < targets.size(); i++) {
+ final DragAndDropPolicy.Target target = targets.get(i);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Add target: %s", target);
+ // Inset the draw region by a little bit
+ target.drawRegion.inset(mDisplayMargin, mDisplayMargin);
+ }
}
+ /**
+ * Updates the visible drop target as the user drags.
+ */
public void update(DragEvent event) {
// Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
// visibility of the current region
- Target target = null;
- for (int i = mTargets.size() - 1; i >= 0; i--) {
- Target t = mTargets.get(i);
- if (t.hitRegion.contains((int) event.getX(), (int) event.getY())) {
- target = t;
- break;
- }
- }
+ DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
+ (int) event.getX(), (int) event.getY());
if (target != null && mCurrentTarget != target) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
Interpolator boundsInterpolator = FAST_OUT_SLOW_IN;
@@ -175,7 +150,11 @@
}
}
+ /**
+ * Hides the drag layout and animates out the visible drop targets.
+ */
public void hide(DragEvent event, Runnable hideCompleteCallback) {
+ mIsShowing = false;
ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
ObjectAnimator boundsAnimator = null;
if (mCurrentTarget != null) {
@@ -199,50 +178,19 @@
mCurrentTarget = null;
}
+ /**
+ * Handles the drop onto a target and animates out the visible drop targets.
+ */
public boolean drop(DragEvent event, SurfaceControl dragSurface,
- Consumer<Rect> dropCompleteCallback) {
+ Runnable dropCompleteCallback) {
+ final boolean handledDrop = mCurrentTarget != null;
mHasDropped = true;
+
+ // Process the drop
+ mPolicy.handleDrop(mCurrentTarget, event.getClipData());
+
// TODO(b/169894807): Coordinate with dragSurface
- final Rect dropRegion = mCurrentTarget != null
- ? mCurrentTarget.dropTargetBounds
- : null;
-
- ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
- ObjectAnimator boundsAnimator = null;
- if (dropRegion != null) {
- Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
- finalBounds.inset(mDisplayMargin, mDisplayMargin);
- mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
- }
-
- if (dropCompleteCallback != null) {
- ObjectAnimator lastAnim = boundsAnimator != null
- ? boundsAnimator
- : alphaAnimator;
- lastAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- dropCompleteCallback.accept(dropRegion);
- }
- });
- }
- return dropRegion != null;
- }
-
- private static class Target {
- final Rect hitRegion;
- final Rect drawRegion;
- final Rect dropTargetBounds;
-
- public Target(Rect hit, Rect draw, Rect drop) {
- hitRegion = hit;
- drawRegion = draw;
- dropTargetBounds = drop;
- }
-
- @Override
- public String toString() {
- return "Target {hit=" + hitRegion + " drop=" + dropTargetBounds + "}";
- }
+ hide(event, dropCompleteCallback);
+ return handledDrop;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java
index 08edc2f..64f7be5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java
@@ -40,7 +40,7 @@
import com.android.wm.shell.R;
/**
- * Drawable to draw the region of the
+ * Drawable to draw the region that the target will have once it is dropped.
*/
public class DropOutlineDrawable extends Drawable {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index fc523aef..c4ce2cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -42,6 +42,7 @@
private ComponentName mLastPipComponentName;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final DisplayLayout mDisplayLayout = new DisplayLayout();
+ private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();
/**
* Set the current PIP bounds.
@@ -151,6 +152,60 @@
mPipReentryState = null;
}
+ public AnimatingBoundsState getAnimatingBoundsState() {
+ return mAnimatingBoundsState;
+ }
+
+ /** Source of truth for the current animation bounds of PIP. */
+ public static class AnimatingBoundsState {
+ /** The bounds used when PIP is being dragged or animated. */
+ private final Rect mTemporaryBounds = new Rect();
+ /** The destination bounds to which PIP is animating. */
+ private final Rect mAnimatingToBounds = new Rect();
+
+ /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */
+ public boolean isAnimating() {
+ return !mTemporaryBounds.isEmpty();
+ }
+
+ /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */
+ public void setTemporaryBounds(Rect bounds) {
+ mTemporaryBounds.set(bounds);
+ }
+
+ /** Set the bounds to which PIP is animating. */
+ public void setAnimatingToBounds(Rect bounds) {
+ mAnimatingToBounds.set(bounds);
+ }
+
+ /** Called when all ongoing dragging and animation operations have ended. */
+ public void onAllAnimationsEnded() {
+ mTemporaryBounds.setEmpty();
+ }
+
+ /** Called when an ongoing physics animation has ended. */
+ public void onPhysicsAnimationEnded() {
+ mAnimatingToBounds.setEmpty();
+ }
+
+ /** Returns the temporary animation bounds. */
+ public Rect getTemporaryBounds() {
+ return mTemporaryBounds;
+ }
+
+ /** Returns the destination bounds to which PIP is currently animating. */
+ public Rect getAnimatingToBounds() {
+ return mAnimatingToBounds;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + AnimatingBoundsState.class.getSimpleName());
+ pw.println(innerPrefix + "mTemporaryBounds=" + mTemporaryBounds);
+ pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds);
+ }
+ }
+
static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
@@ -190,10 +245,12 @@
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+ pw.println(innerPrefix + "mIsStashed=" + mIsStashed);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
mPipReentryState.dump(pw, innerPrefix);
}
+ mAnimatingBoundsState.dump(pw, innerPrefix);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 833924c..39772fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -135,6 +135,7 @@
private final Handler mUpdateHandler;
private final PipBoundsState mPipBoundsState;
private final PipBoundsHandler mPipBoundsHandler;
+ // TODO(b/172286265): Remove dependency on .pip.PHONE.PipMenuActivityController
private final PipMenuActivityController mMenuActivityController;
private final PipAnimationController mPipAnimationController;
private final PipUiEventLogger mPipUiEventLoggerLogger;
@@ -946,10 +947,9 @@
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
.round(tx, mLeash, mState.isInPip());
- if (mMenuActivityController.isMenuVisible()) {
- runOnMainHandler(() -> {
- mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds);
- });
+ if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) {
+ runOnMainHandler(() ->
+ mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds));
} else {
tx.apply();
}
@@ -973,10 +973,9 @@
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
- if (mMenuActivityController.isMenuVisible()) {
- runOnMainHandler(() -> {
- mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds);
- });
+ if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) {
+ runOnMainHandler(() ->
+ mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds));
} else {
tx.apply();
}
@@ -993,7 +992,8 @@
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
return;
- } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
+ } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA
+ && mMenuActivityController != null) {
// TODO: Synchronize this correctly in #applyEnterPipSyncTransaction
finishResizeForMenu(destinationBounds);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 9247c68..83cd63c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -42,7 +42,6 @@
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import java.io.PrintWriter;
import java.util.function.Consumer;
import kotlin.Unit;
@@ -80,20 +79,6 @@
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
- /**
- * Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP
- * using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into
- * and expanding out of the magnetic dismiss target.
- *
- * Once PIP is done being dragged or animated, we set {@link #mBounds} equal to these temporary
- * bounds, and call {@link PipTaskOrganizer#scheduleFinishResizePip} to 'officially' move PIP to
- * its new bounds.
- */
- private final Rect mTemporaryBounds = new Rect();
-
- /** The destination bounds to which PIP is animating. */
- private final Rect mAnimatingToBounds = new Rect();
-
private int mStashOffset = 0;
/** Coordinator instance for resolving conflicts with other floating content. */
@@ -108,15 +93,15 @@
});
/**
- * PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations.
+ * PhysicsAnimator instance for animating {@link PipBoundsState#getAnimatingBoundsState()}
+ * using physics animations.
*/
- private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
- mTemporaryBounds);
+ private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;
private MagnetizedObject<Rect> mMagnetizedPip;
/**
- * Update listener that resizes the PIP to {@link #mTemporaryBounds}.
+ * Update listener that resizes the PIP to {@link PipBoundsState#getAnimatingBoundsState()}.
*/
private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;
@@ -189,14 +174,17 @@
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
+ mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
+ mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
mSfAnimationHandlerThreadLocal.get());
+
reloadResources();
mResizePipUpdateListener = (target, values) -> {
- if (!mTemporaryBounds.isEmpty()) {
- mPipTaskOrganizer.scheduleUserResizePip(
- getBounds(), mTemporaryBounds, null);
+ if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
+ mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
+ mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), null);
}
};
}
@@ -209,7 +197,8 @@
@NonNull
@Override
public Rect getFloatingBoundsOnScreen() {
- return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : getBounds();
+ return !mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds().isEmpty()
+ ? mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds() : getBounds();
}
@NonNull
@@ -227,18 +216,14 @@
* Synchronizes the current bounds with the pinned stack, cancelling any ongoing animations.
*/
void synchronizePinnedStackBounds() {
- cancelAnimations();
- mTemporaryBounds.setEmpty();
+ cancelPhysicsAnimation();
+ mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
if (mPipTaskOrganizer.isInPip()) {
mFloatingContentCoordinator.onContentMoved(this);
}
}
- boolean isAnimating() {
- return mTemporaryBoundsPhysicsAnimator.isRunning();
- }
-
/**
* Tries to move the pinned stack to the given {@param bounds}.
*/
@@ -261,14 +246,14 @@
if (!mSpringingToTouch) {
// If we are moving PIP directly to the touch event locations, cancel any animations and
// move PIP to the given bounds.
- cancelAnimations();
+ cancelPhysicsAnimation();
if (!isDragging) {
resizePipUnchecked(toBounds);
mPipBoundsState.setBounds(toBounds);
} else {
- mTemporaryBounds.set(toBounds);
- mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mTemporaryBounds,
+ mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(toBounds);
+ mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
(Rect newBounds) -> {
mMainHandler.post(() -> {
mMenuController.updateMenuLayout(newBounds);
@@ -303,9 +288,9 @@
final float destinationY = targetCenter.y - (desiredHeight / 2f);
// If we're already in the dismiss target area, then there won't be a move to set the
- // temporary bounds, so just initialize it to the current bounds
- if (mTemporaryBounds.isEmpty()) {
- mTemporaryBounds.set(getBounds());
+ // temporary bounds, so just initialize it to the current bounds.
+ if (!mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
+ mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
}
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
@@ -339,7 +324,7 @@
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
- cancelAnimations();
+ cancelPhysicsAnimation();
mMenuController.hideMenuWithoutResize();
mPipTaskOrganizer.getUpdateHandler().post(() -> {
mPipTaskOrganizer.exitPip(skipAnimation
@@ -356,7 +341,7 @@
if (DEBUG) {
Log.d(TAG, "removePip: callers=\n" + Debug.getCallers(5, " "));
}
- cancelAnimations();
+ cancelPhysicsAnimation();
mMenuController.hideMenuWithoutResize();
mPipTaskOrganizer.removePip();
}
@@ -383,14 +368,6 @@
}
/**
- * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
- * otherwise.
- */
- Rect getPossiblyAnimatingBounds() {
- return mTemporaryBounds.isEmpty() ? getBounds() : mTemporaryBounds;
- }
-
- /**
* Flings the PiP to the closest snap target.
*/
void flingToSnapTarget(
@@ -428,9 +405,10 @@
: mMovementBounds.right;
final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
+
+ final int startValueY = mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds().top;
final float estimatedFlingYEndValue =
- PhysicsAnimator.estimateFlingEndValue(
- mTemporaryBounds.top, velocityY, mFlingConfigY);
+ PhysicsAnimator.estimateFlingEndValue(startValueY, velocityY, mFlingConfigY);
startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */,
false /* dismiss */);
@@ -443,7 +421,7 @@
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
// Animate from the current bounds if we're not already animating.
- mTemporaryBounds.set(getBounds());
+ mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
}
mTemporaryBoundsPhysicsAnimator
@@ -513,7 +491,7 @@
Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
+ " callers=\n" + Debug.getCallers(5, " "));
}
- cancelAnimations();
+ cancelPhysicsAnimation();
mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
mUpdateBoundsCallback);
}
@@ -521,9 +499,9 @@
/**
* Cancels all existing animations.
*/
- private void cancelAnimations() {
+ private void cancelPhysicsAnimation() {
mTemporaryBoundsPhysicsAnimator.cancel();
- mAnimatingToBounds.setEmpty();
+ mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
mSpringingToTouch = false;
}
@@ -547,22 +525,19 @@
*/
private void startBoundsAnimator(float toX, float toY, boolean dismiss) {
if (!mSpringingToTouch) {
- cancelAnimations();
+ cancelPhysicsAnimation();
}
- // Set animatingToBounds directly to avoid allocating a new Rect, but then call
- // setAnimatingToBounds to run the normal logic for changing animatingToBounds.
- mAnimatingToBounds.set(
+ setAnimatingToBounds(new Rect(
(int) toX,
(int) toY,
(int) toX + getBounds().width(),
- (int) toY + getBounds().height());
- setAnimatingToBounds(mAnimatingToBounds);
+ (int) toY + getBounds().height()));
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
mTemporaryBoundsPhysicsAnimator
.addUpdateListener(mResizePipUpdateListener)
- .withEndActions(this::onBoundsAnimationEnd);
+ .withEndActions(this::onBoundsPhysicsAnimationEnd);
}
mTemporaryBoundsPhysicsAnimator.start();
@@ -576,31 +551,34 @@
mDismissalPending = true;
}
- private void onBoundsAnimationEnd() {
+ private void onBoundsPhysicsAnimationEnd() {
+ // The physics animation ended, though we may not necessarily be done animating, such as
+ // when we're still dragging after moving out of the magnetic target.
if (!mDismissalPending
&& !mSpringingToTouch
&& !mMagnetizedPip.getObjectStuckToTarget()) {
- mPipBoundsState.setBounds(mTemporaryBounds);
+ // All animations (including dragging) have actually finished.
+ mPipBoundsState.setBounds(
+ mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
+ mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
if (!mDismissalPending) {
// do not schedule resize if PiP is dismissing, which may cause app re-open to
// mBounds instead of it's normal bounds.
mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
}
- mTemporaryBounds.setEmpty();
}
-
- mAnimatingToBounds.setEmpty();
+ mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
mSpringingToTouch = false;
mDismissalPending = false;
}
/**
- * Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
+ * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
* we return these bounds from
* {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
*/
private void setAnimatingToBounds(Rect bounds) {
- mAnimatingToBounds.set(bounds);
+ mPipBoundsState.getAnimatingBoundsState().setAnimatingToBounds(bounds);
mFloatingContentCoordinator.onContentMoved(this);
}
@@ -639,7 +617,8 @@
MagnetizedObject<Rect> getMagnetizedPip() {
if (mMagnetizedPip == null) {
mMagnetizedPip = new MagnetizedObject<Rect>(
- mContext, mTemporaryBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
+ mContext, mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(),
+ FloatProperties.RECT_X, FloatProperties.RECT_Y) {
@Override
public float getWidth(@NonNull Rect animatedPipBounds) {
return animatedPipBounds.width();
@@ -661,10 +640,4 @@
return mMagnetizedPip;
}
-
- public void dump(PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- pw.println(prefix + TAG);
- pw.println(innerPrefix + "mBounds=" + getBounds());
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index d820e77..c04e7e8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -710,7 +710,7 @@
return;
}
- Rect bounds = mMotionHelper.getPossiblyAnimatingBounds();
+ Rect bounds = getPossiblyAnimatingBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
@@ -747,7 +747,7 @@
mDelta.x += left - lastX;
mDelta.y += top - lastY;
- mTmpBounds.set(mMotionHelper.getPossiblyAnimatingBounds());
+ mTmpBounds.set(getPossiblyAnimatingBounds());
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
@@ -783,7 +783,7 @@
// Reset the touch state on up before the fling settles
mTouchState.reset();
- final Rect animatingBounds = mMotionHelper.getPossiblyAnimatingBounds();
+ final Rect animatingBounds = getPossiblyAnimatingBounds();
// If User releases the PIP window while it's out of the display bounds, put
// PIP into stashed mode.
if (mEnableStash
@@ -872,6 +872,16 @@
|| mExpandedBounds.height() != mNormalBounds.height();
}
+ /**
+ * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
+ * otherwise.
+ */
+ Rect getPossiblyAnimatingBounds() {
+ return mPipBoundsState.getAnimatingBoundsState().isAnimating()
+ ? mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()
+ : mPipBoundsState.getBounds();
+ }
+
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
@@ -889,7 +899,6 @@
pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
mPipBoundsHandler.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
- mMotionHelper.dump(pw, innerPrefix);
if (mPipResizeGestureHandler != null) {
mPipResizeGestureHandler.dump(pw, innerPrefix);
}
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 985dff2..d117673 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
@@ -83,4 +83,9 @@
* @return {@code true} if it successes to split the primary task.
*/
boolean splitPrimaryTask();
+
+ /**
+ * Exits the split to make the primary task fullscreen.
+ */
+ void dismissSplitToPrimaryTask();
}
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 8b616e8..341a459 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
@@ -501,6 +501,11 @@
}
}
+ @Override
+ public void dismissSplitToPrimaryTask() {
+ startDismissSplit(true /* toPrimaryTask */);
+ }
+
/** Notifies the bounds of split screen changed. */
void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
synchronized (mBoundsChangedListeners) {
@@ -519,8 +524,8 @@
mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
}
- void startDismissSplit() {
- mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
+ void startDismissSplit(boolean toPrimaryTask) {
+ mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
updateVisibility(false /* visible */);
mMinimized = false;
// Resets divider bar position to undefined, so new divider bar will apply default position
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
index f709fed..64e9d66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
@@ -271,7 +271,7 @@
Log.d(TAG, " was in split, so this means leave it "
+ mPrimary.topActivityType + " " + mSecondary.topActivityType);
}
- mSplitScreenController.startDismissSplit();
+ mSplitScreenController.startDismissSplit(false /* toPrimaryTask */);
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
// Wasn't in split-mode (both were empty), but now that the primary split is
// populated, we should fully enter split by moving everything else into secondary.
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
index 8b2f668..58fc90e 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -32,8 +32,19 @@
<!-- Workaround grant runtime permission exception from b/152733071 -->
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
<application>
<uses-library android:name="android.test.runner"/>
+
+ <service android:name=".NotificationListener"
+ android:exported="true"
+ android:label="WMShellTestsNotificationListenerService"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
new file mode 100644
index 0000000..ef0390f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -0,0 +1,36 @@
+/*
+ * 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
+
+import android.content.ComponentName
+
+const val IME_WINDOW_NAME = "InputMethod"
+const val PIP_WINDOW_NAME = "PipMenuActivity"
+
+// Test App
+const val TEST_APP_PACKAGE_NAME = "com.android.wm.shell.flicker.testapp"
+// Test App > Pip Activity
+val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".PipActivity")
+const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp"
+const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
+// Test App > Ime Activity
+val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".ImeActivity")
+const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp"
+
+const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 99f824b..75b55c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker
+import android.content.pm.PackageManager
import android.os.RemoteException
import android.os.SystemClock
import android.platform.helpers.IAppHelper
@@ -41,6 +42,9 @@
val uiDevice by lazy {
UiDevice.getInstance(instrumentation)
}
+ val packageManager: PackageManager by lazy {
+ instrumentation.context.getPackageManager()
+ }
/**
* Build a test tag for the test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
new file mode 100644
index 0000000..5c7ec30
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
@@ -0,0 +1,89 @@
+/*
+ * 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
+
+import android.service.notification.NotificationListenerService
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+
+class NotificationListener : NotificationListenerService() {
+ private val notifications: MutableMap<Any, StatusBarNotification> = mutableMapOf()
+
+ override fun onNotificationPosted(sbn: StatusBarNotification) {
+ if (DEBUG) Log.d(TAG, "onNotificationPosted: $sbn")
+ notifications[sbn.key] = sbn
+ }
+
+ override fun onNotificationRemoved(sbn: StatusBarNotification) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved: $sbn")
+ notifications.remove(sbn.key)
+ }
+
+ override fun onListenerConnected() {
+ if (DEBUG) Log.d(TAG, "onListenerConnected")
+ instance = this
+ }
+
+ override fun onListenerDisconnected() {
+ if (DEBUG) Log.d(TAG, "onListenerDisconnected")
+ instance = null
+ notifications.clear()
+ }
+
+ companion object {
+ private const val DEBUG = false
+ private const val TAG = "WMShellFlickerTests_NotificationListener"
+
+ private const val CMD_NOTIFICATION_ALLOW_LISTENER = "cmd notification allow_listener %s"
+ private const val CMD_NOTIFICATION_DISALLOW_LISTENER =
+ "cmd notification disallow_listener %s"
+ private const val COMPONENT_NAME = "com.android.wm.shell.flicker/.NotificationListener"
+
+ private var instance: NotificationListener? = null
+
+ fun startNotificationListener(): Boolean {
+ if (instance != null) {
+ return true
+ }
+
+ runShellCommand(CMD_NOTIFICATION_ALLOW_LISTENER.format(COMPONENT_NAME))
+ return wait { instance != null }
+ }
+
+ fun stopNotificationListener(): Boolean {
+ if (instance == null) {
+ return true
+ }
+
+ runShellCommand(CMD_NOTIFICATION_DISALLOW_LISTENER.format(COMPONENT_NAME))
+ return wait { instance == null }
+ }
+
+ fun waitForNotificationToAppear(predicate: (StatusBarNotification) -> Boolean): Boolean {
+ return instance?.let {
+ wait { it.notifications.values.any(predicate) }
+ } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ }
+
+ fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean {
+ return instance?.let {
+ wait { it.notifications.values.none(predicate) }
+ } ?: throw IllegalStateException("NotificationListenerService is not connected")
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
new file mode 100644
index 0000000..a6d6735
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
@@ -0,0 +1,45 @@
+/*
+ * 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
+
+import android.os.SystemClock
+
+private const val DEFAULT_TIMEOUT = 10000L
+private const val DEFAULT_POLL_INTERVAL = 1000L
+
+fun wait(condition: () -> Boolean): Boolean {
+ val (success, _) = waitForResult(extractor = condition, validator = { it })
+ return success
+}
+
+fun <R> waitForResult(
+ timeout: Long = DEFAULT_TIMEOUT,
+ interval: Long = DEFAULT_POLL_INTERVAL,
+ extractor: () -> R,
+ validator: (R) -> Boolean = { it != null }
+): Pair<Boolean, R?> {
+ val startTime = SystemClock.uptimeMillis()
+ do {
+ val result = extractor()
+ if (validator(result)) {
+ return (true to result)
+ }
+ SystemClock.sleep(interval)
+ } while (SystemClock.uptimeMillis() - startTime < timeout)
+
+ return (false to null)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
new file mode 100644
index 0000000..e8cf7d9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.helpers
+
+import android.app.ActivityManager
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.wm.shell.flicker.TEST_APP_PACKAGE_NAME
+
+abstract class BaseAppHelper(
+ instrumentation: Instrumentation,
+ launcherName: String,
+ private val launcherActivityComponent: ComponentName
+) : StandardAppHelper(
+ instrumentation,
+ TEST_APP_PACKAGE_NAME,
+ launcherName,
+ LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy
+) {
+ protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+
+ private val context: Context
+ get() = mInstrumentation.context
+
+ private val activityManager: ActivityManager?
+ get() = context.getSystemService(ActivityManager::class.java)
+
+ private val appSelector = By.pkg(packageName).depth(0)
+
+ protected val isTelevision: Boolean
+ get() = context.packageManager.run {
+ hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
+ }
+
+ val label: String
+ get() = context.packageManager.run {
+ getApplicationLabel(getApplicationInfo(packageName, 0)).toString()
+ }
+
+ fun launchViaIntent() {
+ context.startActivity(openAppIntent)
+
+ uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS)
+ }
+
+ fun forceStop() = activityManager?.forceStopPackage(packageName)
+
+ override fun getOpenAppIntent(): Intent {
+ val intent = Intent()
+ intent.component = launcherActivityComponent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ return intent
+ }
+
+ companion object {
+ private const val APP_LAUNCH_WAIT_TIME_MS = 10_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
deleted file mode 100644
index 47a62ce..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.helpers
-
-import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-
-abstract class FlickerAppHelper(
- instr: Instrumentation,
- launcherName: String,
- launcherStrategy: ILauncherStrategy
-) : StandardAppHelper(instr, sFlickerPackage, launcherName, launcherStrategy) {
- companion object {
- var sFlickerPackage = "com.android.wm.shell.flicker.testapp"
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 0cedc0a..a6650d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -17,37 +17,36 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.waitForIME
+import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL
import org.junit.Assert
open class ImeAppHelper(
- instr: Instrumentation,
- launcherName: String = "ImeApp",
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : FlickerAppHelper(instr, launcherName, launcherStrategy) {
- open fun openIME(device: UiDevice) {
- val editText = device.wait(
+ instrumentation: Instrumentation
+) : BaseAppHelper(
+ instrumentation,
+ TEST_APP_IME_ACTIVITY_LABEL,
+ TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+) {
+ fun openIME() {
+ val editText = uiDevice.wait(
Until.findObject(By.res(getPackage(), "plain_text_input")),
FIND_TIMEOUT)
Assert.assertNotNull("Text field not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)", editText)
editText.click()
- if (!device.waitForIME()) {
+ if (!uiDevice.waitForIME()) {
Assert.fail("IME did not appear")
}
}
- open fun closeIME(device: UiDevice) {
- device.pressBack()
+ fun closeIME() {
+ uiDevice.pressBack()
// Using only the AccessibilityInfo it is not possible to identify if the IME is active
- device.waitForIdle(1000)
+ uiDevice.waitForIdle(1000)
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 5391702..d5efa40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,29 +17,56 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.os.SystemClock
+import android.view.KeyEvent.KEYCODE_WINDOW
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
-import com.android.server.wm.flicker.helpers.hasPipWindow
+import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.closePipWindow
-import org.junit.Assert
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_LABEL
+import org.junit.Assert.assertNotNull
class PipAppHelper(
- instr: Instrumentation,
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : FlickerAppHelper(instr, "PipApp", launcherStrategy) {
- fun clickEnterPipButton(device: UiDevice) {
- val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
- Assert.assertNotNull("Pip button not found, this usually happens when the device " +
+ instrumentation: Instrumentation
+) : BaseAppHelper(
+ instrumentation,
+ TEST_APP_PIP_ACTIVITY_LABEL,
+ TEST_APP_PIP_ACTIVITY_COMPONENT_NAME
+) {
+ fun clickEnterPipButton() {
+ val enterPipButton = uiDevice.findObject(By.res(packageName, "enter_pip"))
+ assertNotNull("Pip button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)", enterPipButton)
enterPipButton.click()
- device.hasPipWindow()
+
+ // TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
+ if (!isTelevision) {
+ uiDevice.hasPipWindow()
+ } else {
+ // Simply wait for 3 seconds
+ SystemClock.sleep(3_000)
+ }
}
- fun closePipWindow(device: UiDevice) {
- device.closePipWindow()
+ fun closePipWindow() {
+ // TODO(b/172321238): remove this check once and simply call closePipWindow once the TV
+ // logic is integrated there.
+ if (!isTelevision) {
+ uiDevice.closePipWindow()
+ } else {
+ // Bring up Pip menu
+ uiDevice.pressKeyCode(KEYCODE_WINDOW)
+
+ // Wait for the menu to come up and render the close button
+ val closeButton = uiDevice.wait(
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, "close_button")), 3_000)
+ assertNotNull("Pip menu close button is not found", closeButton)
+ closeButton.click()
+
+ // Give it 1 second, just in case
+ SystemClock.sleep(1_000)
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 010aa0d..a1da7c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -31,6 +31,7 @@
import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
import com.android.wm.shell.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.PIP_WINDOW_NAME
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,7 +80,7 @@
}
}
transitions {
- testApp.clickEnterPipButton(device)
+ testApp.clickEnterPipButton()
device.expandPipWindow()
}
assertions {
@@ -89,7 +90,7 @@
all("pipWindowBecomesVisible") {
this.showsAppWindow(testApp.`package`)
.then()
- .showsAppWindow(sPipWindowTitle)
+ .showsAppWindow(PIP_WINDOW_NAME)
}
}
@@ -103,7 +104,7 @@
all("pipLayerBecomesVisible") {
this.showsLayer(testApp.launcherName)
.then()
- .showsLayer(sPipWindowTitle)
+ .showsLayer(PIP_WINDOW_NAME)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 43e0225..d343f2a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -18,7 +18,6 @@
import android.content.ComponentName
import android.graphics.Region
-import android.support.test.launcherhelper.LauncherStrategyFactory
import android.util.Log
import android.view.Surface
import android.view.WindowManager
@@ -29,6 +28,9 @@
import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.flicker.helpers.hasPipWindow
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_COMPONENT_NAME
+import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_WINDOW_NAME
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -51,19 +53,11 @@
private val windowManager: WindowManager =
instrumentation.context.getSystemService(WindowManager::class.java)
- private val keyboardApp = ImeAppHelper(instrumentation, "ImeApp",
- LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy)
-
- private val KEYBOARD_ACTIVITY: ComponentName = ComponentName.createRelative(
- "com.android.wm.shell.flicker.testapp", ".ImeActivity")
- private val PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
- private val INPUT_METHOD_WINDOW_NAME = "InputMethod"
-
- private val testRepetitions = 10
+ private val keyboardApp = ImeAppHelper(instrumentation)
private val keyboardScenario: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
- repeat { testRepetitions }
+ repeat { TEST_REPETITIONS }
// disable layer tracing
withLayerTracing { null }
setup {
@@ -73,11 +67,11 @@
// launch our target pip app
testApp.open()
this.setRotation(rotation)
- testApp.clickEnterPipButton(device)
+ testApp.clickEnterPipButton()
// open an app with an input field and a keyboard
// UiAutomator doesn't support to launch the multiple Activities in a task.
// So use launchActivity() for the Keyboard Activity.
- launchActivity(KEYBOARD_ACTIVITY)
+ launchActivity(TEST_APP_IME_ACTIVITY_COMPONENT_NAME)
}
}
teardown {
@@ -101,10 +95,10 @@
withTestName { testTag }
transitions {
// open the soft keyboard
- keyboardApp.openIME(device)
+ keyboardApp.openIME()
// then close it again
- keyboardApp.closeIME(device)
+ keyboardApp.closeIME()
}
assertions {
windowManagerTrace {
@@ -127,18 +121,18 @@
withTestName { testTag }
transitions {
// open the soft keyboard
- keyboardApp.openIME(device)
+ keyboardApp.openIME()
}
teardown {
eachRun {
// close the keyboard
- keyboardApp.closeIME(device)
+ keyboardApp.closeIME()
}
}
assertions {
windowManagerTrace {
end {
- isAboveWindow(INPUT_METHOD_WINDOW_NAME, PIP_ACTIVITY_WINDOW_NAME)
+ isAboveWindow(IME_WINDOW_NAME, TEST_APP_PIP_ACTIVITY_WINDOW_NAME)
}
}
}
@@ -207,6 +201,8 @@
}
companion object {
+ private const val TEST_REPETITIONS = 10
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
index 3822d69..c1c34ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
@@ -24,8 +24,4 @@
rotation: Int
) : NonRotationTestBase(rotationName, rotation) {
protected val testApp = PipAppHelper(instrumentation)
-
- companion object {
- const val sPipWindowTitle = "PipMenuActivity"
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
new file mode 100644
index 0000000..569da1d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.pip.tv
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.NotificationListener.Companion.startNotificationListener
+import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificationListener
+import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToAppear
+import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToDisappear
+import org.junit.After
+import org.junit.Assert.assertTrue
+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 Pip Notifications on TV.
+ * To run this test: `atest WMShellFlickerTests:TvPipNotificationTests`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class TvPipNotificationTests(rotationName: String, rotation: Int)
+ : TvPipTestBase(rotationName, rotation) {
+
+ @Before
+ override fun setUp() {
+ super.setUp()
+ val started = startNotificationListener()
+ if (!started) {
+ error("NotificationListener hasn't started")
+ }
+ }
+
+ @After
+ override fun tearDown() {
+ stopNotificationListener()
+ testApp.forceStop()
+ super.tearDown()
+ }
+
+ @Test
+ fun pipNotification_postedAndDismissed() {
+ testApp.launchViaIntent()
+ testApp.clickEnterPipButton()
+
+ assertTrue("Pip notification should have been posted",
+ waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) })
+
+ testApp.closePipWindow()
+
+ assertTrue("Pip notification should have been dismissed",
+ waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.label) })
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+}
+
+private const val PIP_NOTIFICATION_TAG = "PipNotification"
+
+private val StatusBarNotification.title: String
+ get() = notification?.extras?.getString(Notification.EXTRA_TITLE) ?: ""
+
+private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean =
+ tag == PIP_NOTIFICATION_TAG && title == expectedTitle
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
new file mode 100644
index 0000000..104248c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.pip.tv
+
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.pip.PipTestBase
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+
+abstract class TvPipTestBase(rotationName: String, rotation: Int)
+ : PipTestBase(rotationName, rotation) {
+
+ private val isTelevision: Boolean
+ get() = packageManager.run {
+ hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
+ }
+
+ @Before
+ open fun setUp() {
+ Assume.assumeTrue(isTelevision)
+ uiDevice.wakeUpAndGoToHomeScreen()
+ }
+
+ @After
+ open fun tearDown() {
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index a8f795e..59d9104 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -22,6 +22,13 @@
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.mock" />
<uses-library android:name="android.test.runner" />
+
+ <activity android:name=".bubbles.BubblesTestActivity"
+ android:allowEmbedded="true"
+ android:documentLaunchMode="always"
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:resizeableActivity="true" />
</application>
<instrumentation
diff --git a/libs/WindowManager/Shell/tests/unittest/res/layout/main.xml b/libs/WindowManager/Shell/tests/unittest/res/layout/main.xml
new file mode 100644
index 0000000..0d09f86
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/res/layout/main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="this is a test activity"
+ />
+ <EditText
+ android:layout_height="wrap_content"
+ android:id="@+id/editText1"
+ android:layout_width="match_parent">
+ <requestFocus></requestFocus>
+ </EditText>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
similarity index 79%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTestCase.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index fdebe4e..5bdf831 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -24,17 +24,18 @@
import androidx.test.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Before;
/**
- * Base class that does One Handed specific setup.
+ * Base class that does shell test case setup.
*/
-public abstract class PipTestCase {
+public abstract class ShellTestCase {
protected TestableContext mContext;
@Before
- public void setup() {
+ public void shellSetup() {
final Context context =
InstrumentationRegistry.getInstrumentation().getTargetContext();
final DisplayManager dm = context.getSystemService(DisplayManager.class);
@@ -47,6 +48,14 @@
.adoptShellPermissionIdentity();
}
+ @After
+ public void shellTearDown() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
protected Context getContext() {
return mContext;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 5c14859..34f772f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -30,7 +30,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -45,8 +44,6 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.HandlerExecutor;
import org.junit.After;
@@ -60,8 +57,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
-public class TaskViewTest extends SysuiTestCase {
+public class TaskViewTest extends ShellTestCase {
@Mock
TaskView.Listener mViewListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 31c08ae..7adc411 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,8 +38,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleData.TimeSource;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.google.common.collect.ImmutableList;
@@ -63,7 +63,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class BubbleDataTest extends SysuiTestCase {
+public class BubbleDataTest extends ShellTestCase {
private BubbleEntry mEntryA1;
private BubbleEntry mEntryA2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
index fd6e2ee..5b77e4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
@@ -30,8 +30,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class BubbleFlyoutViewTest extends SysuiTestCase {
+public class BubbleFlyoutViewTest extends ShellTestCase {
private BubbleFlyoutView mFlyout;
private TextView mFlyoutText;
private TextView mSenderName;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index 690a1ad..0693052 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import static com.google.common.truth.Truth.assertThat;
@@ -37,8 +37,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -49,7 +49,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class BubbleTest extends SysuiTestCase {
+public class BubbleTest extends ShellTestCase {
@Mock
private Notification mNotif;
@Mock
@@ -72,7 +72,7 @@
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, target, 0),
- Icon.createWithResource(mContext, R.drawable.android))
+ Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
.build();
when(mSbn.getNotification()).thenReturn(mNotif);
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
similarity index 80%
copy from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
copy to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
index 43d2ad1..d5fbe55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,21 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.wm.shell.bubbles;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
/**
* Referenced by NotificationTestHelper#makeBubbleMetadata
*/
public class BubblesTestActivity extends Activity {
- public static final String BUBBLE_ACTIVITY_OPENED =
- "com.android.systemui.bubbles.BUBBLE_ACTIVITY_OPENED";
+ public static final String BUBBLE_ACTIVITY_OPENED = "BUBBLE_ACTIVITY_OPENED";
@Override
public void onCreate(Bundle savedInstanceState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index a5bb8ea..9c4f341 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
@@ -34,8 +34,8 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.BubblePositioner;
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.BubblePositioner;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTest.java
index 498330c..c4edbb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index a5f2e8b..a7a7db8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import static org.mockito.Mockito.when;
@@ -30,8 +30,8 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.mockito.Mock;
@@ -50,7 +50,7 @@
*
* See physics-animation-testing.md.
*/
-public class PhysicsAnimationLayoutTestCase extends SysuiTestCase {
+public class PhysicsAnimationLayoutTestCase extends ShellTestCase {
TestablePhysicsAnimationLayout mLayout;
List<View> mViews = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
index 7d0abec..6b01462 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.animation;
+package com.android.wm.shell.bubbles.animation;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -33,8 +33,8 @@
import androidx.dynamicanimation.animation.SpringForce;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
-import com.android.systemui.bubbles.BubblePositioner;
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.FloatingContentCoordinator;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 9b8fd11..4160280 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertTrue
@@ -28,7 +28,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class BubblePersistentRepositoryTest : SysuiTestCase() {
+class BubblePersistentRepositoryTest : ShellTestCase() {
private val bubbles = listOf(
BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 7ea611c..4fab9a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.content.pm.LauncherApps
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
+import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -32,7 +32,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class BubbleVolatileRepositoryTest : SysuiTestCase() {
+class BubbleVolatileRepositoryTest : ShellTestCase() {
private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
index 8cf4534..e0891a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
+package com.android.wm.shell.bubbles.storage
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import org.junit.Test
@@ -28,7 +28,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class BubbleXmlHelperTest : SysuiTestCase() {
+class BubbleXmlHelperTest : ShellTestCase() {
private val bubbles = listOf(
BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
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
new file mode 100644
index 0000000..affd736
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -0,0 +1,358 @@
+/*
+ * 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.draganddrop;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
+import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.view.DisplayInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
+import com.android.wm.shell.splitscreen.DividerView;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+
+/**
+ * Tests for the drag and drop policy.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DragAndDropPolicyTest {
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private IActivityTaskManager mIActivityTaskManager;
+
+ @Mock
+ private SplitScreen mSplitScreen;
+
+ @Mock
+ private DragAndDropPolicy.Starter mStarter;
+
+ private DisplayLayout mDisplayLayout;
+ private Insets mInsets;
+ private DragAndDropPolicy mPolicy;
+
+ private ClipData mActivityClipData;
+ private ClipData mNonResizeableActivityClipData;
+ private ClipData mTaskClipData;
+ private ClipData mShortcutClipData;
+
+ private ActivityManager.RunningTaskInfo mHomeTask;
+ private ActivityManager.RunningTaskInfo mFullscreenAppTask;
+ private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
+ private ActivityManager.RunningTaskInfo mSplitPrimaryAppTask;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ Resources res = mock(Resources.class);
+ Configuration config = new Configuration();
+ doReturn(config).when(res).getConfiguration();
+ DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = 100;
+ info.logicalHeight = 100;
+ mDisplayLayout = new DisplayLayout(info, res, false, false);
+ mInsets = Insets.of(0, 0, 0, 0);
+
+ DividerView divider = mock(DividerView.class);
+ doReturn(divider).when(mSplitScreen).getDividerView();
+ doReturn(new Rect(50, 0, 100, 100)).when(divider)
+ .getNonMinimizedSplitScreenSecondaryBounds();
+
+ mPolicy = new DragAndDropPolicy(mContext, mIActivityTaskManager, mSplitScreen, mStarter);
+ mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
+ mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
+ setClipDataResizeable(mNonResizeableActivityClipData, false);
+ mTaskClipData = createClipData(MIMETYPE_APPLICATION_TASK);
+ mShortcutClipData = createClipData(MIMETYPE_APPLICATION_SHORTCUT);
+
+ mHomeTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ mFullscreenAppTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ mNonResizeableFullscreenAppTask =
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ mNonResizeableFullscreenAppTask.isResizeable = false;
+ mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ ACTIVITY_TYPE_STANDARD);
+
+ setIsPhone(false);
+ setInSplitScreen(false);
+ setRunningTask(mFullscreenAppTask);
+ }
+
+ /**
+ * Creates a clip data that is by default resizeable.
+ */
+ private ClipData createClipData(String mimeType) {
+ ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType });
+ Intent i = new Intent();
+ switch (mimeType) {
+ case MIMETYPE_APPLICATION_SHORTCUT:
+ i.putExtra(Intent.EXTRA_PACKAGE_NAME, "package");
+ i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcut_id");
+ break;
+ case MIMETYPE_APPLICATION_TASK:
+ i.putExtra(Intent.EXTRA_TASK_ID, 12345);
+ break;
+ case MIMETYPE_APPLICATION_ACTIVITY:
+ i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, mock(PendingIntent.class));
+ break;
+ }
+ i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
+ ClipData.Item item = new ClipData.Item(i);
+ item.setActivityInfo(new ActivityInfo());
+ ClipData data = new ClipData(clipDescription, item);
+ setClipDataResizeable(data, true);
+ return data;
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) {
+ ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+ info.configuration.windowConfiguration.setActivityType(actType);
+ info.configuration.windowConfiguration.setWindowingMode(winMode);
+ info.isResizeable = true;
+ return info;
+ }
+
+ private void setRunningTask(ActivityManager.RunningTaskInfo task) throws RemoteException {
+ doReturn(Collections.singletonList(task)).when(mIActivityTaskManager)
+ .getFilteredTasks(anyInt(), anyBoolean());
+ }
+
+ private void setClipDataResizeable(ClipData data, boolean resizeable) {
+ data.getItemAt(0).getActivityInfo().resizeMode = resizeable
+ ? ActivityInfo.RESIZE_MODE_RESIZEABLE
+ : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ }
+
+ private void setIsPhone(boolean isPhone) {
+ Resources res = mock(Resources.class);
+ Configuration config = mock(Configuration.class);
+ config.smallestScreenWidthDp = isPhone ? 400 : 800;
+ doReturn(config).when(res).getConfiguration();
+ doReturn(res).when(mContext).getResources();
+ }
+
+ private void setInSplitScreen(boolean inSplitscreen) {
+ doReturn(inSplitscreen).when(mSplitScreen).isDividerVisible();
+ }
+
+ @Test
+ public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() throws RemoteException {
+ setRunningTask(mHomeTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets()
+ throws RemoteException {
+ setRunningTask(mFullscreenAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
+ // pairs
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ reset(mStarter);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
+ verify(mStarter).enterSplitScreen(anyInt(), eq(false));
+ verify(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets()
+ throws RemoteException {
+ setIsPhone(true);
+ setRunningTask(mFullscreenAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
+ // pairs
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ reset(mStarter);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
+ verify(mStarter).enterSplitScreen(anyInt(), eq(false));
+ verify(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets()
+ throws RemoteException {
+ setRunningTask(mNonResizeableFullscreenAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets()
+ throws RemoteException {
+ setRunningTask(mFullscreenAppTask);
+ mPolicy.start(mDisplayLayout, mNonResizeableActivityClipData);
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() throws RemoteException {
+ setInSplitScreen(true);
+ setRunningTask(mSplitPrimaryAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
+ // pairs
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_RIGHT);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ reset(mStarter);
+
+ // 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(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets()
+ throws RemoteException {
+ setIsPhone(true);
+ setInSplitScreen(true);
+ setRunningTask(mSplitPrimaryAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ // TODO(b/169894807): For now, only allow splitting to the right/bottom until we have split
+ // pairs
+ ArrayList<Target> targets = assertExactTargetTypes(
+ mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_BOTTOM);
+
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ verify(mStarter).startIntent(any(), any());
+ reset(mStarter);
+
+ // 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(mStarter).startIntent(any(), any());
+ }
+
+ @Test
+ public void testTargetHitRects() throws RemoteException {
+ setRunningTask(mFullscreenAppTask);
+ mPolicy.start(mDisplayLayout, mActivityClipData);
+ ArrayList<Target> targets = mPolicy.getTargets(mInsets);
+ for (Target t : targets) {
+ assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t);
+ assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.right - 1, t.hitRegion.top) == t);
+ assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.right - 1, t.hitRegion.bottom - 1)
+ == t);
+ assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.bottom - 1)
+ == t);
+ }
+ }
+
+ private Target filterTargetByType(ArrayList<Target> targets, int type) {
+ for (Target t : targets) {
+ if (type == t.type) {
+ return t;
+ }
+ }
+ fail("Target with type: " + type + " not found");
+ return null;
+ }
+
+ private ArrayList<Target> assertExactTargetTypes(ArrayList<Target> targets,
+ int... expectedTargetTypes) {
+ HashSet<Integer> expected = new HashSet<>();
+ for (int t : expectedTargetTypes) {
+ expected.add(t);
+ }
+ for (Target t : targets) {
+ if (!expected.contains(t.type)) {
+ fail("Found unexpected target type: " + t.type);
+ }
+ expected.remove(t.type);
+ }
+ assertTrue(expected.isEmpty());
+ return targets;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 255e749..55e7a35 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -32,9 +32,7 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipTestCase;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -49,7 +47,7 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class PipAnimationControllerTest extends PipTestCase {
+public class PipAnimationControllerTest extends ShellTestCase {
private PipAnimationController mPipAnimationController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index 37421d9..5169243 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -30,8 +30,7 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.pip.PipBoundsHandler;
-import com.android.wm.shell.pip.PipTestCase;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +45,7 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class PipBoundsHandlerTest extends PipTestCase {
+public class PipBoundsHandlerTest extends ShellTestCase {
private static final int ROUNDING_ERROR_MARGIN = 16;
private static final float ASPECT_RATIO_ERROR_MARGIN = 0.01f;
private static final float DEFAULT_ASPECT_RATIO = 1f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index dc9399e..844f82d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -28,6 +28,8 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +40,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
-public class PipBoundsStateTest extends PipTestCase {
+public class PipBoundsStateTest extends ShellTestCase {
private static final Rect DEFAULT_BOUNDS = new Rect(0, 0, 10, 10);
private static final float DEFAULT_SNAP_FRACTION = 1.0f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 39381c6..efe553a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -40,6 +40,7 @@
import android.window.WindowContainerToken;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -58,7 +59,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class PipTaskOrganizerTest extends PipTestCase {
+public class PipTaskOrganizerTest extends ShellTestCase {
private PipTaskOrganizer mSpiedPipTaskOrganizer;
@Mock private DisplayController mMockdDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 5f0f196..a00a3b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -35,6 +35,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
@@ -42,7 +43,6 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +56,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class PipControllerTest extends PipTestCase {
+public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
@Mock private DisplayController mMockDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 3f60cc0..f6dcec2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -31,17 +31,13 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.pip.PipBoundsHandler;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipTestCase;
import com.android.wm.shell.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.phone.PipMenuActivityController;
-import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.pip.phone.PipResizeGestureHandler;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import org.junit.Before;
import org.junit.Test;
@@ -59,7 +55,7 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class PipTouchHandlerTest extends PipTestCase {
+public class PipTouchHandlerTest extends ShellTestCase {
private PipTouchHandler mPipTouchHandler;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 40667f7..000f7e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -35,8 +35,7 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.pip.PipTestCase;
-import com.android.wm.shell.pip.phone.PipTouchState;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -47,7 +46,7 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
@RunWithLooper
-public class PipTouchStateTest extends PipTestCase {
+public class PipTouchStateTest extends ShellTestCase {
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 903ca2a..a3fcf90 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -160,10 +160,17 @@
"tests/ObbFile_test.cpp",
"tests/PosixUtils_test.cpp",
],
- shared_libs: common_test_libs + ["libbinder", "liblog", "libui"],
+ shared_libs: common_test_libs + [
+ "libbinder",
+ "liblog",
+ "libui",
+ ],
},
host: {
- static_libs: common_test_libs + ["liblog", "libz"],
+ static_libs: common_test_libs + [
+ "liblog",
+ "libz",
+ ],
},
},
data: [
@@ -204,10 +211,20 @@
export_include_dirs: ["include"],
target: {
android: {
- shared_libs: common_test_libs + ["libbinder", "liblog"],
+ shared_libs: common_test_libs + [
+ "libbinder",
+ "liblog",
+ ],
},
host: {
- static_libs: common_test_libs + ["libbinder", "liblog"],
+ static_libs: common_test_libs + [
+ "libbinder",
+ "liblog",
+ ],
+ },
+ darwin: {
+ // libbinder is not supported on mac
+ enabled: false,
},
},
}
diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
index 2dac47b..b36ff09 100644
--- a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp
@@ -27,5 +27,9 @@
"libutils",
],
},
+ darwin: {
+ // libbinder is not supported on mac
+ enabled: false,
+ },
},
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3493693..b61b79e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -76,12 +76,11 @@
* obtain periodic updates of the device's geographical location, or to be notified when the device
* enters the proximity of a given geographical location.
*
- * <p class="note">Unless noted, all Location API methods require the {@link
- * android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link
- * android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. If your application only has the
- * coarse permission then it will not have access to fine location providers. Other providers will
- * still return location results, but the exact location will be obfuscated to a coarse level of
- * accuracy.
+ * <p class="note">Unless otherwise noted, all Location API methods require the
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. If your application only
+ * has the coarse permission then providers will still return location results, but the exact
+ * location will be obfuscated to a coarse level of accuracy.
*/
@SuppressWarnings({"deprecation"})
@SystemService(Context.LOCATION_SERVICE)
@@ -89,6 +88,16 @@
public class LocationManager {
/**
+ * For apps targeting Android S and above, immutable PendingIntents passed into location APIs
+ * will generate an IllegalArgumentException.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ public static final long BLOCK_IMMUTABLE_PENDING_INTENTS = 171317480L;
+
+ /**
* For apps targeting Android S and above, LocationRequest system APIs may not be used with
* PendingIntent location requests.
*
@@ -96,7 +105,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
- public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;
+ public static final long BLOCK_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;
/**
* For apps targeting Android S and above, location clients may receive historical locations
@@ -116,7 +125,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
- private static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L;
+ public static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L;
/**
* For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
@@ -126,7 +135,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
- private static final long TARGETED_PENDING_INTENT = 148963590L;
+ public static final long BLOCK_UNTARGETED_PENDING_INTENTS = 148963590L;
/**
* For apps targeting Android K and above, incomplete locations may not be passed to
@@ -136,7 +145,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
- private static final long INCOMPLETE_LOCATION = 148964793L;
+ public static final long BLOCK_INCOMPLETE_LOCATIONS = 148964793L;
/**
* For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with
@@ -146,7 +155,7 @@
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
- private static final long GPS_STATUS_USAGE = 144027538L;
+ public static final long BLOCK_GPS_STATUS_USAGE = 144027538L;
/**
* Name of the network location provider.
@@ -1372,11 +1381,16 @@
Preconditions.checkArgument(locationRequest != null, "invalid null location request");
Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
- if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ if (Compatibility.isChangeEnabled(BLOCK_UNTARGETED_PENDING_INTENTS)) {
Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
"pending intent must be targeted to a package");
}
+ if (Compatibility.isChangeEnabled(BLOCK_IMMUTABLE_PENDING_INTENTS)) {
+ Preconditions.checkArgument(!pendingIntent.isImmutable(),
+ "pending intent must be mutable");
+ }
+
try {
mService.registerLocationPendingIntent(provider, locationRequest, pendingIntent,
mContext.getPackageName(), mContext.getAttributionTag());
@@ -1729,7 +1743,7 @@
Preconditions.checkArgument(provider != null, "invalid null provider");
Preconditions.checkArgument(location != null, "invalid null location");
- if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) {
+ if (Compatibility.isChangeEnabled(BLOCK_INCOMPLETE_LOCATIONS)) {
Preconditions.checkArgument(location.isComplete(),
"incomplete location object, missing timestamp or accuracy?");
} else {
@@ -1821,29 +1835,38 @@
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. From API version 17 and onwards,
* this method requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
*
- * @param latitude the latitude of the central point of the alert region
- * @param longitude the longitude of the central point of the alert region
- * @param radius the radius of the central point of the alert region in meters
- * @param expiration expiration realtime for this proximity alert in milliseconds, or -1 to
- * indicate no expiration
- * @param intent a {@link PendingIntent} that will sent when entry to or exit from the alert
- * region is detected
+ * @param latitude the latitude of the central point of the alert region
+ * @param longitude the longitude of the central point of the alert region
+ * @param radius the radius of the central point of the alert region in meters
+ * @param expiration expiration realtime for this proximity alert in milliseconds, or -1 to
+ * indicate no expiration
+ * @param pendingIntent a {@link PendingIntent} that will sent when entry to or exit from the
+ * alert region is detected
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
- @NonNull PendingIntent intent) {
- Preconditions.checkArgument(intent != null, "invalid null pending intent");
- if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
- Preconditions.checkArgument(intent.isTargetedToPackage(),
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+
+ if (Compatibility.isChangeEnabled(BLOCK_UNTARGETED_PENDING_INTENTS)) {
+ Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
"pending intent must be targeted to a package");
}
- if (expiration < 0) expiration = Long.MAX_VALUE;
+
+ if (Compatibility.isChangeEnabled(BLOCK_IMMUTABLE_PENDING_INTENTS)) {
+ Preconditions.checkArgument(!pendingIntent.isImmutable(),
+ "pending intent must be mutable");
+ }
+
+ if (expiration < 0) {
+ expiration = Long.MAX_VALUE;
+ }
try {
Geofence fence = Geofence.createCircle(latitude, longitude, radius, expiration);
- mService.requestGeofence(fence, intent, mContext.getPackageName(),
+ mService.requestGeofence(fence, pendingIntent, mContext.getPackageName(),
mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1941,7 +1964,7 @@
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
- if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
throw new UnsupportedOperationException(
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
@@ -1976,7 +1999,7 @@
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addGpsStatusListener(GpsStatus.Listener listener) {
- if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
throw new UnsupportedOperationException(
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
@@ -1996,7 +2019,7 @@
*/
@Deprecated
public void removeGpsStatusListener(GpsStatus.Listener listener) {
- if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
throw new UnsupportedOperationException(
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 4b208ce..5c1f893 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -676,17 +676,9 @@
return null;
}
- /**
- * Instantiate a CA system of the specified system id.
- *
- * @param CA_system_id The system id of the CA system.
- *
- * @throws UnsupportedCasException if the device does not support the
- * specified CA system.
- */
- public MediaCas(int CA_system_id) throws UnsupportedCasException {
+ private void createPlugin(int casSystemId) throws UnsupportedCasException {
try {
- mCasSystemId = CA_system_id;
+ mCasSystemId = casSystemId;
mUserId = ActivityManager.getCurrentUser();
IMediaCasService service = getService();
android.hardware.cas.V1_2.IMediaCasService serviceV12 =
@@ -696,16 +688,16 @@
android.hardware.cas.V1_1.IMediaCasService.castFrom(service);
if (serviceV11 == null) {
Log.d(TAG, "Used cas@1_0 interface to create plugin");
- mICas = service.createPlugin(CA_system_id, mBinder);
+ mICas = service.createPlugin(casSystemId, mBinder);
} else {
Log.d(TAG, "Used cas@1.1 interface to create plugin");
- mICas = mICasV11 = serviceV11.createPluginExt(CA_system_id, mBinder);
+ mICas = mICasV11 = serviceV11.createPluginExt(casSystemId, mBinder);
}
} else {
Log.d(TAG, "Used cas@1.2 interface to create plugin");
mICas = mICasV11 = mICasV12 =
android.hardware.cas.V1_2.ICas
- .castFrom(serviceV12.createPluginExt(CA_system_id, mBinder));
+ .castFrom(serviceV12.createPluginExt(casSystemId, mBinder));
}
} catch(Exception e) {
Log.e(TAG, "Failed to create plugin: " + e);
@@ -713,11 +705,37 @@
} finally {
if (mICas == null) {
throw new UnsupportedCasException(
- "Unsupported CA_system_id " + CA_system_id);
+ "Unsupported casSystemId " + casSystemId);
}
}
}
+ private void registerClient(@NonNull Context context,
+ @Nullable String tvInputServiceSessionId, @PriorityHintUseCaseType int priorityHint) {
+
+ mTunerResourceManager = (TunerResourceManager)
+ context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
+ if (mTunerResourceManager != null) {
+ int[] clientId = new int[1];
+ ResourceClientProfile profile =
+ new ResourceClientProfile(tvInputServiceSessionId, priorityHint);
+ mTunerResourceManager.registerClientProfile(
+ profile, context.getMainExecutor(), mResourceListener, clientId);
+ mClientId = clientId[0];
+ }
+ }
+ /**
+ * Instantiate a CA system of the specified system id.
+ *
+ * @param casSystemId The system id of the CA system.
+ *
+ * @throws UnsupportedCasException if the device does not support the
+ * specified CA system.
+ */
+ public MediaCas(int casSystemId) throws UnsupportedCasException {
+ createPlugin(casSystemId);
+ }
+
/**
* Instantiate a CA system of the specified system id.
*
@@ -733,19 +751,35 @@
public MediaCas(@NonNull Context context, int casSystemId,
@Nullable String tvInputServiceSessionId,
@PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException {
- this(casSystemId);
-
Objects.requireNonNull(context, "context must not be null");
- mTunerResourceManager = (TunerResourceManager)
- context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
- if (mTunerResourceManager != null) {
- int[] clientId = new int[1];
- ResourceClientProfile profile =
- new ResourceClientProfile(tvInputServiceSessionId, priorityHint);
- mTunerResourceManager.registerClientProfile(
- profile, context.getMainExecutor(), mResourceListener, clientId);
- mClientId = clientId[0];
- }
+ createPlugin(casSystemId);
+ registerClient(context, tvInputServiceSessionId, priorityHint);
+ }
+ /**
+ * Instantiate a CA system of the specified system id with EvenListener.
+ *
+ * @param context the context of the caller.
+ * @param casSystemId The system id of the CA system.
+ * @param tvInputServiceSessionId The Id of the session opened in TV Input Service (TIS)
+ * {@link android.media.tv.TvInputService#onCreateSession(String, String)}
+ * @param priorityHint priority hint from the use case type for new created CAS system.
+ * @param listener the event listener to be set.
+ * @param handler the handler whose looper the event listener will be called on.
+ * If handler is null, we'll try to use current thread's looper, or the main
+ * looper. If neither are available, an internal thread will be created instead.
+ *
+ * @throws UnsupportedCasException if the device does not support the
+ * specified CA system.
+ */
+ public MediaCas(@NonNull Context context, int casSystemId,
+ @Nullable String tvInputServiceSessionId,
+ @PriorityHintUseCaseType int priorityHint,
+ @Nullable Handler handler, @Nullable EventListener listener)
+ throws UnsupportedCasException {
+ Objects.requireNonNull(context, "context must not be null");
+ setEventListener(listener, handler);
+ createPlugin(casSystemId);
+ registerClient(context, tvInputServiceSessionId, priorityHint);
}
IHwBinder getBinder() {
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index df5e85e..da69c6c 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -520,11 +520,12 @@
@Override
public void applyVolumeShaper(
- @NonNull VolumeShaper.Configuration configuration,
- @NonNull VolumeShaper.Operation operation) {
+ @NonNull VolumeShaperConfiguration configuration,
+ @NonNull VolumeShaperOperation operation) {
final PlayerBase pb = mWeakPB.get();
if (pb != null) {
- pb.playerApplyVolumeShaper(configuration, operation);
+ pb.playerApplyVolumeShaper(VolumeShaper.Configuration.fromParcelable(configuration),
+ VolumeShaper.Operation.fromParcelable(operation));
}
}
}
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
index 5f3997a..ec39128 100644
--- a/media/java/android/media/PlayerProxy.java
+++ b/media/java/android/media/PlayerProxy.java
@@ -143,7 +143,8 @@
@NonNull VolumeShaper.Configuration configuration,
@NonNull VolumeShaper.Operation operation) {
try {
- mConf.getIPlayer().applyVolumeShaper(configuration, operation);
+ mConf.getIPlayer().applyVolumeShaper(configuration.toParcelable(),
+ operation.toParcelable());
} catch (NullPointerException|RemoteException e) {
throw new IllegalStateException(
"No player to proxy for applyVolumeShaper operation,"
diff --git a/media/java/android/media/VolumeShaper.java b/media/java/android/media/VolumeShaper.java
index df8d08e..5bad693 100644
--- a/media/java/android/media/VolumeShaper.java
+++ b/media/java/android/media/VolumeShaper.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+import android.os.BadParcelableException;
import android.os.Parcel;
import android.os.Parcelable;
@@ -482,50 +483,62 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- // this needs to match the native VolumeShaper.Configuration parceling
- dest.writeInt(mType);
- dest.writeInt(mId);
- if (mType != TYPE_ID) {
- dest.writeInt(mOptionFlags);
- dest.writeDouble(mDurationMs);
- // this needs to match the native Interpolator parceling
- dest.writeInt(mInterpolatorType);
- dest.writeFloat(0.f); // first slope (specifying for native side)
- dest.writeFloat(0.f); // last slope (specifying for native side)
- // mTimes and mVolumes should have the same length.
- dest.writeInt(mTimes.length);
- for (int i = 0; i < mTimes.length; ++i) {
- dest.writeFloat(mTimes[i]);
- dest.writeFloat(mVolumes[i]);
- }
- }
+ VolumeShaperConfiguration parcelable = toParcelable();
+ parcelable.writeToParcel(dest, flags);
}
- public static final @android.annotation.NonNull Parcelable.Creator<VolumeShaper.Configuration> CREATOR
- = new Parcelable.Creator<VolumeShaper.Configuration>() {
- @Override
- public VolumeShaper.Configuration createFromParcel(Parcel p) {
- // this needs to match the native VolumeShaper.Configuration parceling
- final int type = p.readInt();
- final int id = p.readInt();
- if (type == TYPE_ID) {
- return new VolumeShaper.Configuration(id);
- } else {
- final int optionFlags = p.readInt();
- final double durationMs = p.readDouble();
- // this needs to match the native Interpolator parceling
- final int interpolatorType = p.readInt();
- final float firstSlope = p.readFloat(); // ignored on the Java side
- final float lastSlope = p.readFloat(); // ignored on the Java side
- final int length = p.readInt();
- final float[] times = new float[length];
- final float[] volumes = new float[length];
- for (int i = 0; i < length; ++i) {
- times[i] = p.readFloat();
- volumes[i] = p.readFloat();
- }
+ /** @hide */
+ public VolumeShaperConfiguration toParcelable() {
+ VolumeShaperConfiguration parcelable = new VolumeShaperConfiguration();
+ parcelable.type = typeToAidl(mType);
+ parcelable.id = mId;
+ if (mType != TYPE_ID) {
+ parcelable.optionFlags = optionFlagsToAidl(mOptionFlags);
+ parcelable.durationMs = mDurationMs;
+ parcelable.interpolatorConfig = toInterpolatorParcelable();
+ }
+ return parcelable;
+ }
- return new VolumeShaper.Configuration(
+ private InterpolatorConfig toInterpolatorParcelable() {
+ InterpolatorConfig parcelable = new InterpolatorConfig();
+ parcelable.type = interpolatorTypeToAidl(mInterpolatorType);
+ parcelable.firstSlope = 0.f; // first slope (specifying for native side)
+ parcelable.lastSlope = 0.f; // last slope (specifying for native side)
+ parcelable.xy = new float[mTimes.length * 2];
+ for (int i = 0; i < mTimes.length; ++i) {
+ parcelable.xy[i * 2] = mTimes[i];
+ parcelable.xy[i * 2 + 1] = mVolumes[i];
+ }
+ return parcelable;
+ }
+
+ /** @hide */
+ public static Configuration fromParcelable(VolumeShaperConfiguration parcelable) {
+ // this needs to match the native VolumeShaper.Configuration parceling
+ final int type = typeFromAidl(parcelable.type);
+ final int id = parcelable.id;
+ if (type == TYPE_ID) {
+ return new VolumeShaper.Configuration(id);
+ } else {
+ final int optionFlags = optionFlagsFromAidl(parcelable.optionFlags);
+ final double durationMs = parcelable.durationMs;
+ final int interpolatorType = interpolatorTypeFromAidl(
+ parcelable.interpolatorConfig.type);
+ // parcelable.interpolatorConfig.firstSlope is ignored on the Java side
+ // parcelable.interpolatorConfig.lastSlope is ignored on the Java side
+ final int length = parcelable.interpolatorConfig.xy.length;
+ if (length % 2 != 0) {
+ throw new android.os.BadParcelableException("xy length must be even");
+ }
+ final float[] times = new float[length / 2];
+ final float[] volumes = new float[length / 2];
+ for (int i = 0; i < length / 2; ++i) {
+ times[i] = parcelable.interpolatorConfig.xy[i * 2];
+ volumes[i] = parcelable.interpolatorConfig.xy[i * 2 + 1];
+ }
+
+ return new VolumeShaper.Configuration(
type,
id,
optionFlags,
@@ -533,7 +546,14 @@
interpolatorType,
times,
volumes);
- }
+ }
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<VolumeShaper.Configuration> CREATOR
+ = new Parcelable.Creator<VolumeShaper.Configuration>() {
+ @Override
+ public VolumeShaper.Configuration createFromParcel(Parcel p) {
+ return fromParcelable(VolumeShaperConfiguration.CREATOR.createFromParcel(p));
}
@Override
@@ -542,6 +562,84 @@
}
};
+ private static @InterpolatorType
+ int interpolatorTypeFromAidl(@android.media.InterpolatorType int aidl) {
+ switch (aidl) {
+ case android.media.InterpolatorType.STEP:
+ return INTERPOLATOR_TYPE_STEP;
+ case android.media.InterpolatorType.LINEAR:
+ return INTERPOLATOR_TYPE_LINEAR;
+ case android.media.InterpolatorType.CUBIC:
+ return INTERPOLATOR_TYPE_CUBIC;
+ case android.media.InterpolatorType.CUBIC_MONOTONIC:
+ return INTERPOLATOR_TYPE_CUBIC_MONOTONIC;
+ default:
+ throw new BadParcelableException("Unknown interpolator type");
+ }
+ }
+
+ private static @android.media.InterpolatorType
+ int interpolatorTypeToAidl(@InterpolatorType int type) {
+ switch (type) {
+ case INTERPOLATOR_TYPE_STEP:
+ return android.media.InterpolatorType.STEP;
+ case INTERPOLATOR_TYPE_LINEAR:
+ return android.media.InterpolatorType.LINEAR;
+ case INTERPOLATOR_TYPE_CUBIC:
+ return android.media.InterpolatorType.CUBIC;
+ case INTERPOLATOR_TYPE_CUBIC_MONOTONIC:
+ return android.media.InterpolatorType.CUBIC_MONOTONIC;
+ default:
+ throw new RuntimeException("Unknown interpolator type");
+ }
+ }
+
+ private static @Type
+ int typeFromAidl(@android.media.VolumeShaperConfigurationType int aidl) {
+ switch (aidl) {
+ case VolumeShaperConfigurationType.ID:
+ return TYPE_ID;
+ case VolumeShaperConfigurationType.SCALE:
+ return TYPE_SCALE;
+ default:
+ throw new BadParcelableException("Unknown type");
+ }
+ }
+
+ private static @android.media.VolumeShaperConfigurationType
+ int typeToAidl(@Type int type) {
+ switch (type) {
+ case TYPE_ID:
+ return VolumeShaperConfigurationType.ID;
+ case TYPE_SCALE:
+ return VolumeShaperConfigurationType.SCALE;
+ default:
+ throw new RuntimeException("Unknown type");
+ }
+ }
+
+ private static int optionFlagsFromAidl(int aidl) {
+ int result = 0;
+ if ((aidl & (1 << VolumeShaperConfigurationOptionFlag.VOLUME_IN_DBFS)) != 0) {
+ result |= OPTION_FLAG_VOLUME_IN_DBFS;
+ }
+ if ((aidl & (1 << VolumeShaperConfigurationOptionFlag.CLOCK_TIME)) != 0) {
+ result |= OPTION_FLAG_CLOCK_TIME;
+ }
+ return result;
+ }
+
+ private static int optionFlagsToAidl(int flags) {
+ int result = 0;
+ if ((flags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
+ result |= (1 << VolumeShaperConfigurationOptionFlag.VOLUME_IN_DBFS);
+ }
+ if ((flags & OPTION_FLAG_CLOCK_TIME) != 0) {
+ result |= (1 << VolumeShaperConfigurationOptionFlag.CLOCK_TIME);
+ }
+ return result;
+ }
+
/**
* @hide
* Constructs a {@code VolumeShaper} from an id.
@@ -1172,25 +1270,31 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- // this needs to match the native VolumeShaper.Operation parceling
- dest.writeInt(mFlags);
- dest.writeInt(mReplaceId);
- dest.writeFloat(mXOffset);
+ toParcelable().writeToParcel(dest, flags);
+ }
+
+ /** @hide */
+ public VolumeShaperOperation toParcelable() {
+ VolumeShaperOperation result = new VolumeShaperOperation();
+ result.flags = flagsToAidl(mFlags);
+ result.replaceId = mReplaceId;
+ result.xOffset = mXOffset;
+ return result;
+ }
+
+ /** @hide */
+ public static Operation fromParcelable(VolumeShaperOperation parcelable) {
+ return new VolumeShaper.Operation(
+ flagsFromAidl(parcelable.flags),
+ parcelable.replaceId,
+ parcelable.xOffset);
}
public static final @android.annotation.NonNull Parcelable.Creator<VolumeShaper.Operation> CREATOR
= new Parcelable.Creator<VolumeShaper.Operation>() {
@Override
public VolumeShaper.Operation createFromParcel(Parcel p) {
- // this needs to match the native VolumeShaper.Operation parceling
- final int flags = p.readInt();
- final int replaceId = p.readInt();
- final float xOffset = p.readFloat();
-
- return new VolumeShaper.Operation(
- flags
- , replaceId
- , xOffset);
+ return fromParcelable(VolumeShaperOperation.CREATOR.createFromParcel(p));
}
@Override
@@ -1199,6 +1303,46 @@
}
};
+ private static int flagsFromAidl(int aidl) {
+ int result = 0;
+ if ((aidl & (1 << VolumeShaperOperationFlag.REVERSE)) != 0) {
+ result |= FLAG_REVERSE;
+ }
+ if ((aidl & (1 << VolumeShaperOperationFlag.TERMINATE)) != 0) {
+ result |= FLAG_TERMINATE;
+ }
+ if ((aidl & (1 << VolumeShaperOperationFlag.JOIN)) != 0) {
+ result |= FLAG_JOIN;
+ }
+ if ((aidl & (1 << VolumeShaperOperationFlag.DELAY)) != 0) {
+ result |= FLAG_DEFER;
+ }
+ if ((aidl & (1 << VolumeShaperOperationFlag.CREATE_IF_NECESSARY)) != 0) {
+ result |= FLAG_CREATE_IF_NEEDED;
+ }
+ return result;
+ }
+
+ private static int flagsToAidl(int flags) {
+ int result = 0;
+ if ((flags & FLAG_REVERSE) != 0) {
+ result |= (1 << VolumeShaperOperationFlag.REVERSE);
+ }
+ if ((flags & FLAG_TERMINATE) != 0) {
+ result |= (1 << VolumeShaperOperationFlag.TERMINATE);
+ }
+ if ((flags & FLAG_JOIN) != 0) {
+ result |= (1 << VolumeShaperOperationFlag.JOIN);
+ }
+ if ((flags & FLAG_DEFER) != 0) {
+ result |= (1 << VolumeShaperOperationFlag.DELAY);
+ }
+ if ((flags & FLAG_CREATE_IF_NEEDED) != 0) {
+ result |= (1 << VolumeShaperOperationFlag.CREATE_IF_NECESSARY);
+ }
+ return result;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Operation(@Flag int flags, int replaceId, float xOffset) {
mFlags = flags;
@@ -1393,17 +1537,27 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeFloat(mVolume);
- dest.writeFloat(mXOffset);
+ toParcelable().writeToParcel(dest, flags);
+ }
+
+ /** @hide */
+ public VolumeShaperState toParcelable() {
+ VolumeShaperState result = new VolumeShaperState();
+ result.volume = mVolume;
+ result.xOffset = mXOffset;
+ return result;
+ }
+
+ /** @hide */
+ public static State fromParcelable(VolumeShaperState p) {
+ return new VolumeShaper.State(p.volume, p.xOffset);
}
public static final @android.annotation.NonNull Parcelable.Creator<VolumeShaper.State> CREATOR
= new Parcelable.Creator<VolumeShaper.State>() {
@Override
public VolumeShaper.State createFromParcel(Parcel p) {
- return new VolumeShaper.State(
- p.readFloat() // volume
- , p.readFloat()); // xOffset
+ return fromParcelable(VolumeShaperState.CREATOR.createFromParcel(p));
}
@Override
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 1881e38..5a578dd 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -122,6 +122,17 @@
android.hardware.tv.tuner.V1_1.Constants.Constant
.INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
/**
+ * Invalid first macroblock address in MmtpRecordEvent and TsRecordEvent.
+ *
+ * <p>Returned by {@link MmtpRecordEvent#getMbInSlice()} and
+ * {@link TsRecordEvent#getMbInSlice()} when the requested sequence number is not available.
+ *
+ * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMbInSlice()
+ * @see android.media.tv.tuner.filter.TsRecordEvent#getMbInSlice()
+ */
+ public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FIRST_MACROBLOCK_IN_SLICE;
+ /**
* Invalid local transport stream id.
*
* <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed
@@ -1062,6 +1073,13 @@
}
}
+ private void onDvbcAnnexReported(int dvbcAnnex) {
+ if (mScanCallbackExecutor != null && mScanCallback != null) {
+ mScanCallbackExecutor.execute(
+ () -> mScanCallback.onDvbcAnnexReported(dvbcAnnex));
+ }
+ }
+
/**
* Opens a filter object based on the given types and buffer size.
*
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 7060bd72..f0abce9 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -31,13 +31,16 @@
private final long mDataLength;
private final int mMpuSequenceNumber;
private final long mPts;
+ private final int mFirstMbInSlice;
// This constructor is used by JNI code only
- private MmtpRecordEvent(int scHevcIndexMask, long dataLength, int mpuSequenceNumber, long pts) {
+ private MmtpRecordEvent(int scHevcIndexMask, long dataLength, int mpuSequenceNumber, long pts,
+ int firstMbInSlice) {
mScHevcIndexMask = scHevcIndexMask;
mDataLength = dataLength;
mMpuSequenceNumber = mpuSequenceNumber;
mPts = pts;
+ mFirstMbInSlice = firstMbInSlice;
}
/**
@@ -58,6 +61,11 @@
/**
* Get the MPU sequence number of the filtered data.
+ *
+ * <p>This field is only supported in Tuner 1.1 or higher version. Unsupported version will
+ * return {@link android.media.tv.tuner.Tuner.INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM}. Use
+ * {@link android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version
+ * information.
*/
public int getMpuSequenceNumber() {
return mMpuSequenceNumber;
@@ -65,10 +73,26 @@
/**
* Get the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
- * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
- * the SC_HEVC.
+ * and has the same format as the PTS in ISO/IEC 13818-1.
+ *
+ * <p>This field is only supported in Tuner 1.1 or higher version. Unsupported version will
+ * return {@link android.media.tv.tuner.Tuner.INVALID_TIMESTAMP}. Use
+ * {@link android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version
+ * information.
*/
public long getPts() {
return mPts;
}
+
+ /**
+ * Get the address of the first macroblock in the slice defined in ITU-T Rec. H.264.
+ *
+ * <p>This field is only supported in Tuner 1.1 or higher version. Unsupported version will
+ * return {@link android.media.tv.tuner.Tuner.INVALID_FIRST_MACROBLOCK_IN_SLICE}. Use
+ * {@link android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version
+ * information.
+ */
+ public int getFirstMbInSlice() {
+ return mFirstMbInSlice;
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index ec01e44..a6fd20e 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -133,8 +133,11 @@
* according to ISO/IEC 13818-1.
* @hide
*/
- @IntDef(flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME,
- SC_INDEX_SEQUENCE})
+ @IntDef(prefix = "SC_INDEX_",
+ flag = true,
+ value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME,
+ SC_INDEX_SEQUENCE, SC_INDEX_I_SLICE, SC_INDEX_P_SLICE,
+ SC_INDEX_B_SLICE, SC_INDEX_SI_SLICE, SC_INDEX_SP_SLICE})
@Retention(RetentionPolicy.SOURCE)
public @interface ScIndex {}
@@ -154,7 +157,31 @@
* SC index for a new sequence.
*/
public static final int SC_INDEX_SEQUENCE = Constants.DemuxScIndex.SEQUENCE;
-
+ /**
+ * All blocks are coded as I blocks.
+ */
+ public static final int SC_INDEX_I_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.I_SLICE;
+ /**
+ * Blocks are coded as I or P blocks.
+ */
+ public static final int SC_INDEX_P_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.P_SLICE;
+ /**
+ * Blocks are coded as I, P or B blocks.
+ */
+ public static final int SC_INDEX_B_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.B_SLICE;
+ /**
+ * A so-called switching I slice that is coded.
+ */
+ public static final int SC_INDEX_SI_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.SI_SLICE;
+ /**
+ * A so-called switching P slice that is coded.
+ */
+ public static final int SC_INDEX_SP_SLICE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.SP_SLICE;
/**
* Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index 258e2f2..6ea3bf8 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -33,14 +33,17 @@
private final int mScIndexMask;
private final long mDataLength;
private final long mPts;
+ private final int mFirstMbInSlice;
// This constructor is used by JNI code only
- private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength, long pts) {
+ private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength, long pts,
+ int firstMbInSlice) {
mPid = pid;
mTsIndexMask = tsIndexMask;
mScIndexMask = scIndexMask;
mDataLength = dataLength;
mPts = pts;
+ mFirstMbInSlice = firstMbInSlice;
}
/**
@@ -77,10 +80,26 @@
/**
* Gets the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
- * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
- * the SC_HEVC.
+ * and has the same format as the PTS in ISO/IEC 13818-1.
+ *
+ * <p>This field is only supported in Tuner 1.1 or higher version. Unsupported version will
+ * return {@link android.media.tv.tuner.Tuner.INVALID_TIMESTAMP}. Use
+ * {@link android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version
+ * information.
*/
public long getPts() {
return mPts;
}
+
+ /**
+ * Get the address of the first macroblock in the slice defined in ITU-T Rec. H.264.
+ *
+ * <p>This field is only supported in Tuner 1.1 or higher version. Unsupported version will
+ * return {@link android.media.tv.tuner.Tuner.INVALID_FIRST_MACROBLOCK_IN_SLICE}. Use
+ * {@link android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version
+ * information.
+ */
+ public int getFirstMbInSlice() {
+ return mFirstMbInSlice;
+ }
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index fadc004..98f8096 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -341,12 +341,13 @@
return mScanType;
}
/**
- * To receive Diseqc Message or not. Default value is false.
+ * Get if the client could handle the Diseqc Rx Message or not. Default value is false.
*
- * The setter {@link Builder#setDiseqcRxMessage(boolean)} is only supported with Tuner HAL 1.1
- * or higher.
+ * The setter {@link Builder#setCouldHandleDiseqcRxMessage(boolean)} is only supported with
+ * Tuner HAL 1.1 or higher. Use {@link TunerVersionChecker.getTunerVersion()} to check the
+ * version.
*/
- public boolean isDiseqcRxMessage() {
+ public boolean getCouldHandleDiseqcRxMessage() {
return mIsDiseqcRxMessage;
}
@@ -408,16 +409,18 @@
}
/**
- * Set true to receive Diseqc Message.
+ * Set true to indicate the client could handle the Diseqc Messages. Note that it's still
+ * possible that the client won't receive the messages when HAL is not able to setup Rx
+ * channel in the hardware layer.
*
* <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
* no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
- public Builder setDiseqcRxMessage(boolean isDiseqcRxMessage) {
+ public Builder setCouldHandleDiseqcRxMessage(boolean couldReceiveDiseqcMessage) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_1_1, "setDiseqcRxMessage")) {
- mIsDiseqcRxMessage = isDiseqcRxMessage;
+ TunerVersionChecker.TUNER_VERSION_1_1, "setCouldHandleDiseqcRxMessage")) {
+ mIsDiseqcRxMessage = couldReceiveDiseqcMessage;
}
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2147622..f8470b11 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -302,6 +302,7 @@
*
* @return the end frequency in Hz.
*/
+ @IntRange(from = 1)
public int getEndFrequency() {
return mEndFrequency;
}
@@ -341,11 +342,15 @@
*
* @param endFrequency the end frequency used during blind scan. The default value is
* {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}.
+ * @throws IllegalArgumentException if the {@code endFrequency} is not greater than 0.
*/
@IntRange(from = 1)
public void setEndFrequency(int endFrequency) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "setEndFrequency")) {
+ if (endFrequency < 1) {
+ throw new IllegalArgumentException("endFrequency must be greater than 0");
+ }
mEndFrequency = endFrequency;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 9bf7a5d..27627d7 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -75,4 +75,7 @@
/** Frontend scan message priority reported. */
default void onPriorityReported(boolean isHighPriority) {}
+
+ /** DVBC Frontend Annex reported. */
+ default void onDvbcAnnexReported(@DvbcFrontendSettings.Annex int dvbcAnnex) {}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 724965d..c7fb50f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,6 +31,8 @@
],
shared_libs: [
+ "audioclient-types-aidl-unstable-cpp",
+ "av-types-aidl-unstable-cpp",
"libandroid_runtime",
"libaudioclient",
"libnativehelper",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 27e1992..758f015 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -605,9 +605,13 @@
jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts)
: static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+ jlong firstMbInSlice = (eventsExt.size() > i)
+ ? static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice)
+ : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
jobject obj =
- env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts);
+ env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber,
+ pts, firstMbInSlice);
env->SetObjectArrayElement(arr, i, obj);
}
return arr;
@@ -632,10 +636,13 @@
: static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts)
: static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+ jlong firstMbInSlice = (eventsExt.size() > i)
+ ? static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice)
+ : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
jobject obj =
env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
- mpuSequenceNumber, pts);
+ mpuSequenceNumber, pts, firstMbInSlice);
env->SetObjectArrayElement(arr, i, obj);
}
return arr;
@@ -1058,10 +1065,18 @@
bool isHighPriority = message.isHighPriority();
env->CallVoidMethod(
mObject,
- env->GetMethodID(clazz, "onPriorityReported", "([B)V"),
+ env->GetMethodID(clazz, "onPriorityReported", "(B)V"),
isHighPriority);
break;
}
+ case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: {
+ jint dvbcAnnex = (jint) message.annex();
+ env->CallVoidMethod(
+ mObject,
+ env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"),
+ dvbcAnnex);
+ break;
+ }
default:
break;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 3def945..ec1240f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -17,7 +17,6 @@
package com.android.systemui;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.car.navigationbar.CarNavigationBar;
import com.android.systemui.car.notification.CarNotificationModule;
import com.android.systemui.car.sideloaded.SideLoadedAppController;
@@ -48,8 +47,7 @@
/** Binder for car specific {@link SystemUI} modules. */
@Module(includes = {RecentsModule.class, StatusBarModule.class, NotificationsModule.class,
- BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class,
- CarNotificationModule.class})
+ KeyguardModule.class, OverlayWindowModule.class, CarNotificationModule.class})
public abstract class CarSystemUIBinder {
/** Inject into AuthController. */
@Binds
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6a4c8c3..48421ce 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1385,4 +1385,9 @@
<!-- Name of the 3.5mm and usb audio device. [CHAR LIMIT=50] -->
<string name="media_transfer_wired_usb_device_name">Wired headphone</string>
+
+ <!-- Label for Wifi hotspot switch on. Toggles hotspot on [CHAR LIMIT=30] -->
+ <string name="wifi_hotspot_switch_on_text">On</string>
+ <!-- Label for Wifi hotspot switch off. Toggles hotspot off [CHAR LIMIT=30] -->
+ <string name="wifi_hotspot_switch_off_text">Off</string>
</resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f9268eec..39a6ed1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -352,7 +352,7 @@
android:exported="true" />
<activity
- android:name=".bubbles.BubbleOverflowActivity"
+ android:name="com.android.wm.shell.bubbles.BubbleOverflowActivity"
android:theme="@style/BubbleOverflow"
android:excludeFromRecents="true"
android:documentLaunchMode="always"
diff --git a/packages/SystemUI/res/drawable/bubble_dismiss_circle.xml b/packages/SystemUI/res/drawable/bubble_dismiss_circle.xml
deleted file mode 100644
index 8c7e82f..0000000
--- a/packages/SystemUI/res/drawable/bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<!--
- The transparent circle outline that encircles the bubbles when they're in the dismiss target.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <stroke
- android:width="1dp"
- android:color="#66FFFFFF" />
-
- <solid android:color="#B3000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bubble_dismiss_icon.xml b/packages/SystemUI/res/drawable/bubble_dismiss_icon.xml
deleted file mode 100644
index 5c8de58..0000000
--- a/packages/SystemUI/res/drawable/bubble_dismiss_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<!-- The 'X' bubble dismiss icon. This is just ic_close with a stroke. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"
- android:strokeColor="#FF000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_create_bubble.xml b/packages/SystemUI/res/drawable/ic_create_bubble.xml
deleted file mode 100644
index 4abbc81..0000000
--- a/packages/SystemUI/res/drawable/ic_create_bubble.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="20dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FF000000"
- android:pathData="M23,5v8h-2V5H3v14h10v2v0H3c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h18C22.1,3 23,3.9 23,5zM10,8v2.59L5.71,6.29L4.29,7.71L8.59,12H6v2h6V8H10zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_screenshot_scroll.xml b/packages/SystemUI/res/drawable/ic_screenshot_scroll.xml
new file mode 100644
index 0000000..c260ba9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_scroll.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<!-- ic_unfold_more_24px.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zM12,18.17L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_stop_bubble.xml b/packages/SystemUI/res/drawable/ic_stop_bubble.xml
deleted file mode 100644
index 6cf67a7..0000000
--- a/packages/SystemUI/res/drawable/ic_stop_bubble.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="20dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FF000000"
- android:pathData="M11.29,14.71L7,10.41V13H5V7h6v2H8.41l4.29,4.29L11.29,14.71zM21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h10v0v-2H3V5h18v8h2V5C23,3.9 22.1,3 21,3zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
-</vector>
diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
index 040303a..71b414f 100644
--- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
- android:contentDescription="@string/screenshot_edit"
+ android:contentDescription="@string/screenshot_edit_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 1b5f9c1..6c20c1e 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -46,7 +46,7 @@
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
android:elevation="7dp"
android:visibility="gone"
- android:contentDescription="@string/screenshot_dismiss_ui_description">
+ android:contentDescription="@string/screenshot_dismiss_description">
<ImageView
android:id="@+id/global_screenshot_dismiss_image"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml
index c745854..5262407 100644
--- a/packages/SystemUI/res/layout/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
- android:contentDescription="@string/screenshot_edit"
+ android:contentDescription="@string/screenshot_edit_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index 26edf3a..096ec7d 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -56,6 +56,9 @@
android:id="@+id/screenshot_share_chip"/>
<include layout="@layout/global_screenshot_action_chip"
android:id="@+id/screenshot_edit_chip"/>
+ <include layout="@layout/global_screenshot_action_chip"
+ android:id="@+id/screenshot_scroll_chip"
+ android:visibility="gone" />
</LinearLayout>
</HorizontalScrollView>
<include layout="@layout/global_screenshot_preview"/>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index b584dfe..e45f4eb 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -36,10 +36,6 @@
<dimen name="volume_tool_tip_right_margin">136dp</dimen>
<dimen name="volume_tool_tip_top_margin">12dp</dimen>
- <!-- Padding between status bar and bubbles when displayed in expanded state, smaller
- value in landscape since we have limited vertical space-->
- <dimen name="bubble_padding_top">4dp</dimen>
-
<dimen name="controls_activity_view_top_offset">25dp</dimen>
<dimen name="biometric_dialog_button_negative_max_width">140dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 6df8b4e..be36316 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -204,10 +204,6 @@
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
- <!-- Bubbles -->
- <color name="bubbles_light">#FFFFFF</color>
- <color name="bubbles_dark">@color/GM2_grey_800</color>
-
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 17dc400..a2e9f39 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1159,90 +1159,6 @@
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
-
- <!-- How much each bubble is elevated. -->
- <dimen name="bubble_elevation">1dp</dimen>
- <!-- How much the bubble flyout text container is elevated. -->
- <dimen name="bubble_flyout_elevation">4dp</dimen>
- <!-- How much padding is around the left and right sides of the flyout text. -->
- <dimen name="bubble_flyout_padding_x">12dp</dimen>
- <!-- How much padding is around the top and bottom of the flyout text. -->
- <dimen name="bubble_flyout_padding_y">10dp</dimen>
- <!-- Size of the triangle that points from the flyout to the bubble stack. -->
- <dimen name="bubble_flyout_pointer_size">6dp</dimen>
- <!-- How much space to leave between the flyout (tip of the arrow) and the bubble stack. -->
- <dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
- <!-- How much space to leave between the flyout text and the avatar displayed in the flyout. -->
- <dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
- <!-- Padding between status bar and bubbles when displayed in expanded state -->
- <dimen name="bubble_padding_top">16dp</dimen>
- <!-- Size of individual bubbles. -->
- <dimen name="individual_bubble_size">60dp</dimen>
- <!-- Size of bubble bitmap. -->
- <dimen name="bubble_bitmap_size">52dp</dimen>
- <!-- Size of bubble icon bitmap. -->
- <dimen name="bubble_overflow_icon_bitmap_size">24dp</dimen>
- <!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
- <dimen name="bubble_touch_padding">12dp</dimen>
- <!-- Size of the circle around the bubbles when they're in the dismiss target. -->
- <dimen name="bubble_dismiss_encircle_size">52dp</dimen>
- <!-- Padding around the view displayed when the bubble is expanded -->
- <dimen name="bubble_expanded_view_padding">4dp</dimen>
- <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
- a slight touch slop around the expanded view. -->
- <dimen name="bubble_expanded_view_slop">8dp</dimen>
- <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
- <dimen name="bubble_expanded_default_height">180dp</dimen>
- <!-- Default height of bubble overflow -->
- <dimen name="bubble_overflow_height">480dp</dimen>
- <!-- Bubble overflow padding when there are no bubbles -->
- <dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
- <!-- Padding of container for overflow bubbles -->
- <dimen name="bubble_overflow_padding">15dp</dimen>
- <!-- Padding of label for bubble overflow view -->
- <dimen name="bubble_overflow_text_padding">7dp</dimen>
- <!-- Height of bubble overflow empty state illustration -->
- <dimen name="bubble_empty_overflow_image_height">200dp</dimen>
- <!-- Padding of bubble overflow empty state subtitle -->
- <dimen name="bubble_empty_overflow_subtitle_padding">50dp</dimen>
- <!-- Height of the triangle that points to the expanded bubble -->
- <dimen name="bubble_pointer_height">8dp</dimen>
- <!-- Width of the triangle that points to the expanded bubble -->
- <dimen name="bubble_pointer_width">12dp</dimen>
- <!-- Extra padding around the dismiss target for bubbles -->
- <dimen name="bubble_dismiss_slop">16dp</dimen>
- <!-- Height of button allowing users to adjust settings for bubbles. -->
- <dimen name="bubble_manage_button_height">48dp</dimen>
- <!-- Max width of the message bubble-->
- <dimen name="bubble_message_max_width">144dp</dimen>
- <!-- Min width of the message bubble -->
- <dimen name="bubble_message_min_width">32dp</dimen>
- <!-- Interior padding of the message bubble -->
- <dimen name="bubble_message_padding">4dp</dimen>
- <!-- Offset between bubbles in their stacked position. -->
- <dimen name="bubble_stack_offset">10dp</dimen>
- <!-- Offset between stack y and animation y for bubble swap. -->
- <dimen name="bubble_swap_animation_offset">15dp</dimen>
- <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
- <dimen name="bubble_stack_offscreen">9dp</dimen>
- <!-- How far down the screen the stack starts. -->
- <dimen name="bubble_stack_starting_offset_y">120dp</dimen>
- <!-- Space between the pointer triangle and the bubble expanded view -->
- <dimen name="bubble_pointer_margin">8dp</dimen>
- <!-- Padding applied to the bubble dismiss target. Touches in this padding cause the bubbles to
- snap to the dismiss target. -->
- <dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
- <dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
- <dimen name="bubble_manage_menu_elevation">4dp</dimen>
-
- <!-- Bubbles user education views -->
- <dimen name="bubbles_manage_education_width">160dp</dimen>
- <!-- The inset from the top bound of the manage button to place the user education. -->
- <dimen name="bubbles_manage_education_top_inset">65dp</dimen>
- <!-- Size of padding for the user education cling, this should at minimum be larger than
- individual_bubble_size + some padding. -->
- <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
-
<!-- Size of the RAT type for CellularTile -->
<dimen name="celltile_rat_type_size">10sp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 7d3135a..5f68bdb4 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -130,12 +130,6 @@
<item type="id" name="action_snooze_assistant_suggestion_1"/>
<item type="id" name="action_snooze"/>
- <!-- Accessibility actions for bubbles. -->
- <item type="id" name="action_move_top_left"/>
- <item type="id" name="action_move_top_right"/>
- <item type="id" name="action_move_bottom_left"/>
- <item type="id" name="action_move_bottom_right"/>
-
<!-- For StatusIconContainer to tag its icon views -->
<item type="id" name="status_bar_view_state_tag" />
@@ -146,17 +140,6 @@
<!-- Optional cancel button on Keyguard -->
<item type="id" name="cancel_button"/>
- <!-- For saving PhysicsAnimationLayout animations/animators as view tags. -->
- <item type="id" name="translation_x_dynamicanimation_tag"/>
- <item type="id" name="translation_y_dynamicanimation_tag"/>
- <item type="id" name="translation_z_dynamicanimation_tag"/>
- <item type="id" name="alpha_dynamicanimation_tag"/>
- <item type="id" name="scale_x_dynamicanimation_tag"/>
- <item type="id" name="scale_y_dynamicanimation_tag"/>
- <item type="id" name="physics_animator_tag"/>
- <item type="id" name="target_animator_tag" />
- <item type="id" name="reorder_animator_tag"/>
-
<!-- Global Actions Menu -->
<item type="id" name="global_actions_view" />
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index b1e91c8..b50b5c1 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -22,17 +22,6 @@
<integer name="qs_footer_actions_width">0</integer>
<integer name="qs_footer_actions_weight">1</integer>
- <!-- Maximum number of bubbles to render and animate at one time. While the animations used are
- lightweight translation animations, this number can be reduced on lower end devices if any
- performance issues arise. -->
- <integer name="bubbles_max_rendered">5</integer>
-
- <!-- Number of columns in bubble overflow. -->
- <integer name="bubbles_overflow_columns">4</integer>
-
- <!-- Maximum number of bubbles we allow in overflow before we dismiss the oldest one. -->
- <integer name="bubbles_max_overflow">16</integer>
-
<integer name="magnification_default_scale">2</integer>
<!-- The position of the volume dialog on the screen.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d5c9823..773ef7d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -233,10 +233,16 @@
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
+ <!-- Label for UI element which allows editing the screenshot [CHAR LIMIT=30] -->
+ <string name="screenshot_edit_label">Edit</string>
<!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
- <string name="screenshot_edit">Edit screenshot</string>
+ <string name="screenshot_edit_description">Edit screenshot</string>
+ <!-- Label for UI element which allows scrolling and extending the screenshot to be taller [CHAR LIMIT=30] -->
+ <string name="screenshot_scroll_label">Scroll</string>
+ <!-- Content description UI element which allows scrolling and extending the screenshot to be taller [CHAR LIMIT=NONE] -->
+ <string name="screenshot_scroll_description">Scroll screenshot</string>
<!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
- <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
+ <string name="screenshot_dismiss_description">Dismiss screenshot</string>
<!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
<string name="screenshot_preview_description">Screenshot preview</string>
@@ -646,9 +652,6 @@
<!-- Content description to tell the user a notification has been removed from the notification shade -->
<string name="accessibility_notification_dismissed">Notification dismissed.</string>
- <!-- Content description to tell the user a bubble has been dismissed. -->
- <string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
-
<!-- Content description for the notification shade panel (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_notification_shade">Notification shade.</string>
<!-- Content description for the quick settings panel (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -1846,9 +1849,6 @@
<string name="notification_alert_title">Default</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
- <string name="notification_bubble_title">Bubble</string>
-
- <!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_automatic_title">Automatic</string>
<!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary -->
@@ -1881,12 +1881,6 @@
<!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
<string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> doesn\u2019t support conversation features</string>
- <!-- [CHAR LIMIT=NONE] Empty overflow title -->
- <string name="bubble_overflow_empty_title">No recent bubbles</string>
-
- <!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
- <string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string>
-
<!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
<string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
@@ -2607,45 +2601,8 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
- <!-- Text used for content description of settings button in the header of expanded bubble
- view. [CHAR_LIMIT=NONE] -->
- <string name="bubbles_settings_button_description">Settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubbles</string>
- <!-- Content description for button that shows bubble overflow on click [CHAR LIMIT=NONE] -->
- <string name="bubble_overflow_button_content_description">Overflow</string>
- <!-- Action to add overflow bubble back to stack. [CHAR LIMIT=NONE] -->
- <string name="bubble_accessibility_action_add_back">Add back to stack</string>
- <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
- <string name="manage_bubbles_text">Manage</string>
- <!-- Content description when a bubble is focused. [CHAR LIMIT=NONE] -->
- <string name="bubble_content_description_single"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g></string>
- <!-- Content description when the stack of bubbles is focused. [CHAR LIMIT=NONE] -->
- <string name="bubble_content_description_stack"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g> and <xliff:g id="bubble_count" example="4">%3$d</xliff:g> more</string>
<!-- Action in accessibility menu to move the stack of bubbles [CHAR LIMIT=20] -->
<string name="bubble_accessibility_action_move">Move</string>
- <!-- Action in accessibility menu to move the stack of bubbles to the top left of the screen. [CHAR LIMIT=30] -->
- <string name="bubble_accessibility_action_move_top_left">Move top left</string>
- <!-- Action in accessibility menu to move the stack of bubbles to the top right of the screen. [CHAR LIMIT=30] -->
- <string name="bubble_accessibility_action_move_top_right">Move top right</string>
- <!-- Action in accessibility menu to move the stack of bubbles to the bottom left of the screen. [CHAR LIMIT=30]-->
- <string name="bubble_accessibility_action_move_bottom_left">Move bottom left</string>
- <!-- Action in accessibility menu to move the stack of bubbles to the bottom right of the screen. [CHAR LIMIT=30]-->
- <string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
- <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
- <string name="bubble_dismiss_text">Dismiss bubble</string>
- <!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
- <string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
- <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
- <string name="bubbles_user_education_title">Chat using bubbles</string>
- <!-- Descriptive text for the bubble feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] -->
- <string name="bubbles_user_education_description">New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it.</string>
- <!-- Title text for the bubble "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]-->
- <string name="bubbles_user_education_manage_title">Control bubbles anytime</string>
- <!-- Descriptive text for the bubble "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]-->
- <string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
- <!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]-->
- <string name="bubbles_user_education_got_it">Got it</string>
- <!-- Label for the button that takes the user to the notification settings for the given app. -->
- <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] -->
<string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 628193d..5b89f7f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
@@ -30,6 +31,9 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.util.ViewController;
@@ -51,6 +55,7 @@
private final ClockManager mClockManager;
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
+ private FrameLayout mNewLockscreenClockFrame;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
@@ -114,6 +119,7 @@
mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors());
updateAodIcons();
+ mNewLockscreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
}
@Override
@@ -180,6 +186,21 @@
}
/**
+ * Update position of the view, with optional animation. Move the slice view and the clock
+ * slightly towards the center in order to prevent burn-in. Y positioning occurs at the
+ * view parent level.
+ */
+ void updatePosition(int x, AnimationProperties props, boolean animate) {
+ x = Math.abs(x);
+ if (mNewLockscreenClockFrame != null) {
+ PropertyAnimator.setProperty(mNewLockscreenClockFrame, AnimatableProperty.TRANSLATION_X,
+ -x, props, animate);
+ }
+ mKeyguardSliceViewController.updatePosition(x, props, animate);
+ mNotificationIconAreaController.updatePosition(x, props, animate);
+ }
+
+ /**
* Update lockscreen mode that may change clock display.
*/
void updateLockScreenMode(int mode) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 8b55b06..02b18b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -42,6 +42,9 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
@@ -199,6 +202,13 @@
Trace.endSection();
}
+ /**
+ * Update position of the view, with optional animation
+ */
+ void updatePosition(int x, AnimationProperties props, boolean animate) {
+ PropertyAnimator.setProperty(mView, AnimatableProperty.TRANSLATION_X, x, props, animate);
+ }
+
void showSlice(Slice slice) {
Trace.beginSection("KeyguardSliceViewController#showSlice");
if (slice == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index cc0d1b6..cc7b832 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -186,12 +186,23 @@
/**
* Update position of the view with an optional animation
*/
- public void updatePosition(int clockTranslationX, int clockTranslationY,
- boolean animateClock) {
- PropertyAnimator.setProperty(mView, AnimatableProperty.X,
- clockTranslationX, CLOCK_ANIMATION_PROPERTIES, animateClock);
- PropertyAnimator.setProperty(mView, AnimatableProperty.Y,
- clockTranslationY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ public void updatePosition(int x, int y, boolean animate) {
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
+ animate);
+
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+ // reset any prior movement
+ PropertyAnimator.setProperty(mView, AnimatableProperty.X, 0,
+ CLOCK_ANIMATION_PROPERTIES, animate);
+
+ mKeyguardClockSwitchController.updatePosition(x, CLOCK_ANIMATION_PROPERTIES, animate);
+ } else {
+ // reset any prior movement
+ mKeyguardClockSwitchController.updatePosition(0, CLOCK_ANIMATION_PROPERTIES, animate);
+
+ PropertyAnimator.setProperty(mView, AnimatableProperty.X, x,
+ CLOCK_ANIMATION_PROPERTIES, animate);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0e6bc24..31b0701 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -289,6 +289,8 @@
private final DevicePolicyManager mDevicePolicyManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private boolean mLogoutEnabled;
+ // cached value to avoid IPCs
+ private boolean mIsUdfpsEnrolled;
// If the user long pressed the lock icon, disabling face auth for the current session.
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1857,7 +1859,15 @@
private void updateLockScreenMode() {
mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_LOCKSCREEN, mAuthController.isUdfpsEnrolled() ? 1 : 0);
+ Settings.Global.SHOW_NEW_LOCKSCREEN,
+ isUdfpsEnrolled() ? 1 : 0);
+ }
+
+ private void updateUdfpsEnrolled(int userId) {
+ mIsUdfpsEnrolled = mAuthController.isUdfpsEnrolled(userId);
+ }
+ public boolean isUdfpsEnrolled() {
+ return mIsUdfpsEnrolled;
}
private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
@@ -2098,6 +2108,7 @@
}
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = getCurrentUser();
+ updateUdfpsEnrolled(userId);
if (isUnlockWithFingerprintPossible(userId)) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
@@ -3069,6 +3080,7 @@
+ " expected=" + (shouldListenForFingerprint() ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
}
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index b30103e..17bb40e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -108,12 +108,14 @@
.setPip(mWMComponent.getPip())
.setSplitScreen(mWMComponent.getSplitScreen())
.setOneHanded(mWMComponent.getOneHanded())
+ .setBubbles(mWMComponent.getBubbles())
.setShellDump(mWMComponent.getShellDump());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components.
builder = builder.setPip(Optional.ofNullable(null))
.setSplitScreen(Optional.ofNullable(null))
.setOneHanded(Optional.ofNullable(null))
+ .setBubbles(Optional.ofNullable(null))
.setShellDump(Optional.ofNullable(null));
}
mSysUIComponent = builder
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index c72bc25..a6b1b90 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -56,6 +56,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@@ -81,6 +82,7 @@
@Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
+ @Nullable private final List<FingerprintSensorPropertiesInternal> mUdfpsProps;
// TODO: These should just be saved from onSaveState
private SomeArgs mCurrentDialogArgs;
@@ -314,6 +316,16 @@
: null;
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
+ List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
+ if (mFpProps != null) {
+ for (FingerprintSensorPropertiesInternal props : mFpProps) {
+ if (props.isAnyUdfpsType()) {
+ udfpsProps.add(props);
+ }
+ }
+ }
+ mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -326,15 +338,9 @@
mCommandQueue.addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
- final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
- mFingerprintManager.getSensorPropertiesInternal();
- for (FingerprintSensorPropertiesInternal props : fingerprintSensorProperties) {
- if (props.isAnyUdfpsType()) {
- mUdfpsController = mUdfpsControllerFactory.get();
- break;
- }
- }
+ if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()
+ && mUdfpsProps != null) {
+ mUdfpsController = mUdfpsControllerFactory.get();
}
try {
@@ -484,12 +490,14 @@
}
/**
- * Whether the current user has a UDFP enrolled.
+ * Whether the passed userId has enrolled UDFPS.
*/
- public boolean isUdfpsEnrolled() {
- // TODO: (b/171392825) right now only checks whether the UDFPS sensor exists on this device
- // but not whether user has enrolled or not
- return mUdfpsController != null;
+ public boolean isUdfpsEnrolled(int userId) {
+ if (mUdfpsController == null) {
+ return false;
+ }
+
+ return mFingerprintManager.hasEnrolledTemplatesForAnySensor(userId, mUdfpsProps);
}
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
deleted file mode 100644
index ffb650d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2019 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.bubbles;
-
-import static android.app.Notification.EXTRA_MESSAGES;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
-
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.people.PeopleHubNotificationListenerKt;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Common class for experiments controlled via secure settings.
- */
-public class BubbleExperimentConfig {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
-
- private static final int BUBBLE_HEIGHT = 10000;
-
- private static final String ALLOW_ANY_NOTIF_TO_BUBBLE = "allow_any_notif_to_bubble";
- private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
-
- private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
- private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
-
- private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
- private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
-
- private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
-
- /**
- * When true, if a notification has the information necessary to bubble (i.e. valid
- * contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
- * object will be created by the system and added to the notification.
- * <p>
- * This does not produce a bubble, only adds the metadata based on the notification info.
- */
- static boolean allowAnyNotifToBubble(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_ANY_NOTIF_TO_BUBBLE,
- ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
- * Same as {@link #allowAnyNotifToBubble(Context)} except it filters for notifications that
- * are using {@link Notification.MessagingStyle} and have remote input.
- */
- static boolean allowMessageNotifsToBubble(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_MESSAGE_NOTIFS_TO_BUBBLE,
- ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
- * When true, if the notification is able to bubble via {@link #allowAnyNotifToBubble(Context)}
- * or {@link #allowMessageNotifsToBubble(Context)} or via normal BubbleMetadata, then a new
- * BubbleMetadata object is constructed based on the shortcut info.
- * <p>
- * This does not produce a bubble, only adds the metadata based on shortcut info.
- */
- static boolean useShortcutInfoToBubble(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_SHORTCUTS_TO_BUBBLE,
- ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
- * Returns whether the provided package is whitelisted to bubble.
- */
- static boolean isPackageWhitelistedToAutoBubble(Context context, String packageName) {
- String unsplitList = Settings.Secure.getString(context.getContentResolver(),
- WHITELISTED_AUTO_BUBBLE_APPS);
- if (unsplitList != null) {
- // We expect the list to be separated by commas and no white space (but we trim in case)
- String[] packageList = unsplitList.split(",");
- for (int i = 0; i < packageList.length; i++) {
- if (packageList[i].trim().equals(packageName)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
- * {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
- * the notification has necessary info for BubbleMetadata.
- *
- * @return whether an adjustment was made.
- */
- static boolean adjustForExperiments(Context context, NotificationEntry entry,
- boolean previouslyUserCreated, boolean userBlocked) {
- Notification.BubbleMetadata metadata = null;
- boolean addedMetadata = false;
- boolean whiteListedToAutoBubble =
- isPackageWhitelistedToAutoBubble(context, entry.getSbn().getPackageName());
-
- Notification notification = entry.getSbn().getNotification();
- boolean isMessage = Notification.MessagingStyle.class.equals(
- notification.getNotificationStyle());
- boolean bubbleNotifForExperiment = (isMessage && allowMessageNotifsToBubble(context))
- || allowAnyNotifToBubble(context);
-
- boolean useShortcutInfo = useShortcutInfoToBubble(context);
- String shortcutId = entry.getSbn().getNotification().getShortcutId();
-
- boolean hasMetadata = entry.getBubbleMetadata() != null;
- if ((!hasMetadata && (previouslyUserCreated || bubbleNotifForExperiment))
- || useShortcutInfo) {
- if (DEBUG_EXPERIMENTS) {
- Log.d(TAG, "Adjusting " + entry.getKey() + " for bubble experiment."
- + " allowMessages=" + allowMessageNotifsToBubble(context)
- + " isMessage=" + isMessage
- + " allowNotifs=" + allowAnyNotifToBubble(context)
- + " useShortcutInfo=" + useShortcutInfo
- + " previouslyUserCreated=" + previouslyUserCreated);
- }
- }
-
- if (useShortcutInfo && shortcutId != null) {
- // We don't actually get anything useful from ShortcutInfo so just check existence
- ShortcutInfo info = getShortcutInfo(context, entry.getSbn().getPackageName(),
- entry.getSbn().getUser(), shortcutId);
- if (info != null) {
- metadata = createForShortcut(shortcutId);
- }
-
- // Replace existing metadata with shortcut, or we're bubbling for experiment
- boolean shouldBubble = entry.getBubbleMetadata() != null
- || bubbleNotifForExperiment
- || previouslyUserCreated;
- if (shouldBubble && metadata != null) {
- if (DEBUG_EXPERIMENTS) {
- Log.d(TAG, "Adding experimental shortcut bubble for: " + entry.getKey());
- }
- entry.setBubbleMetadata(metadata);
- addedMetadata = true;
- }
- }
-
- // Didn't get metadata from a shortcut & we're bubbling for experiment
- if (entry.getBubbleMetadata() == null
- && (bubbleNotifForExperiment || previouslyUserCreated)) {
- metadata = createFromNotif(context, entry);
- if (metadata != null) {
- if (DEBUG_EXPERIMENTS) {
- Log.d(TAG, "Adding experimental notification bubble for: " + entry.getKey());
- }
- entry.setBubbleMetadata(metadata);
- addedMetadata = true;
- }
- }
-
- boolean bubbleForWhitelist = !userBlocked
- && whiteListedToAutoBubble
- && (addedMetadata || hasMetadata);
- if ((previouslyUserCreated && addedMetadata) || bubbleForWhitelist) {
- // Update to a previous bubble (or new autobubble), set its flag now.
- if (DEBUG_EXPERIMENTS) {
- Log.d(TAG, "Setting FLAG_BUBBLE for: " + entry.getKey());
- }
- entry.setFlagBubble(true);
- return true;
- }
- return addedMetadata;
- }
-
- static Notification.BubbleMetadata createFromNotif(Context context, NotificationEntry entry) {
- Notification notification = entry.getSbn().getNotification();
- final PendingIntent intent = notification.contentIntent;
- Icon icon = null;
- // Use the icon of the person if available
- List<Person> personList = getPeopleFromNotification(entry);
- if (personList.size() > 0) {
- final Person person = personList.get(0);
- if (person != null) {
- icon = person.getIcon();
- if (icon == null) {
- // Lets try and grab the icon constructed by the layout
- Drawable d = PeopleHubNotificationListenerKt.extractAvatarFromRow(entry);
- if (d instanceof BitmapDrawable) {
- icon = Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
- }
- }
- }
- }
- if (icon == null) {
- boolean shouldTint = notification.getLargeIcon() == null;
- icon = shouldTint
- ? notification.getSmallIcon()
- : notification.getLargeIcon();
- if (shouldTint) {
- int notifColor = entry.getSbn().getNotification().color;
- notifColor = ColorUtils.setAlphaComponent(notifColor, 255);
- notifColor = ContrastColorUtil.findContrastColor(notifColor, Color.WHITE,
- true /* findFg */, 3f);
- icon.setTint(notifColor);
- }
- }
- if (intent != null) {
- return new Notification.BubbleMetadata.Builder(intent, icon)
- .setDesiredHeight(BUBBLE_HEIGHT)
- .build();
- }
- return null;
- }
-
- static Notification.BubbleMetadata createForShortcut(String shortcutId) {
- return new Notification.BubbleMetadata.Builder(shortcutId)
- .setDesiredHeight(BUBBLE_HEIGHT)
- .build();
- }
-
- static ShortcutInfo getShortcutInfo(Context context, String packageName, UserHandle user,
- String shortcutId) {
- LauncherApps launcherAppService =
- (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST);
- List<ShortcutInfo> shortcuts = launcherAppService.getShortcuts(query, user);
- return shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- }
-
- static List<Person> getPeopleFromNotification(NotificationEntry entry) {
- Bundle extras = entry.getSbn().getNotification().extras;
- ArrayList<Person> personList = new ArrayList<>();
- if (extras == null) {
- return personList;
- }
-
- List<Person> p = extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST);
-
- if (p != null) {
- personList.addAll(p);
- }
-
- if (Notification.MessagingStyle.class.equals(
- entry.getSbn().getNotification().getNotificationStyle())) {
- final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
- if (!ArrayUtils.isEmpty(messages)) {
- for (Notification.MessagingStyle.Message message :
- Notification.MessagingStyle.Message
- .getMessagesFromBundleArray(messages)) {
- personList.add(message.getSenderPerson());
- }
- }
- }
- return personList;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
deleted file mode 100644
index 5a7e033..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ /dev/null
@@ -1,98 +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.bubbles.dagger;
-
-import android.app.INotificationManager;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.os.Handler;
-import android.view.WindowManager;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.Bubbles;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.wmshell.BubblesManager;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.common.FloatingContentCoordinator;
-
-import java.util.Optional;
-
-import dagger.Module;
-import dagger.Provides;
-
-/** */
-@Module
-public interface BubbleModule {
-
- /**
- */
- @SysUISingleton
- @Provides
- static Bubbles newBubbleController(Context context,
- FloatingContentCoordinator floatingContentCoordinator,
- IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps,
- UiEventLogger uiEventLogger,
- @Main Handler mainHandler,
- ShellTaskOrganizer organizer) {
- return BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator,
- statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- uiEventLogger, mainHandler, organizer);
- }
-
- /** Provides Optional of BubbleManager */
- @SysUISingleton
- @Provides
- static Optional<BubblesManager> provideBubblesManager(Context context,
- Optional<Bubbles> bubblesOptional,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarStateController statusBarStateController, ShadeController shadeController,
- ConfigurationController configurationController,
- @Nullable IStatusBarService statusBarService, INotificationManager notificationManager,
- NotificationInterruptStateProvider interruptionStateProvider,
- ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
- NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
- NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
- DumpManager dumpManager) {
- return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
- notificationShadeWindowController, statusBarStateController, shadeController,
- configurationController, statusBarService, notificationManager,
- interruptionStateProvider, zenModeController, notifUserManager,
- groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index 554d9cb..53383d6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -22,9 +22,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
-import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.animation.FlingAnimationUtils;
-import com.android.wm.shell.common.FloatingContentCoordinator;
import javax.inject.Singleton;
@@ -51,22 +49,6 @@
GlobalConcurrencyModule.class})
public class GlobalModule {
- // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
- // Bubbles has migrated over
- @Singleton
- @Provides
- static FloatingContentCoordinator provideFloatingContentCoordinator() {
- return new FloatingContentCoordinator();
- }
-
- // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once
- // Bubbles has migrated over
- @Singleton
- @Provides
- static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
- return new WindowManagerShellWrapper();
- }
-
// TODO(b/162923491): This should not be a singleton at all, the display metrics can change and
// callers should be creating a new builder on demand
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d73633e..b94a68b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -27,6 +27,7 @@
import com.android.systemui.util.InjectionInflationController;
import com.android.wm.shell.ShellDump;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -64,6 +65,9 @@
Builder setOneHanded(Optional<OneHanded> o);
@BindsInstance
+ Builder setBubbles(Optional<Bubbles> b);
+
+ @BindsInstance
Builder setInputConsumerController(InputConsumerController i);
@BindsInstance
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 1f6288a..c0013d8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -24,7 +24,6 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -51,8 +50,7 @@
/**
* SystemUI objects that are injectable should go here.
*/
-@Module(includes = {RecentsModule.class, StatusBarModule.class, BubbleModule.class,
- KeyguardModule.class})
+@Module(includes = {RecentsModule.class, StatusBarModule.class, KeyguardModule.class})
public abstract class SystemUIBinder {
/** Inject into AuthController. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a982ec5..780bb5b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -16,32 +16,49 @@
package com.android.systemui.dagger;
+import android.app.INotificationManager;
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
import com.android.systemui.tuner.dagger.TunerModule;
@@ -53,6 +70,10 @@
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
import com.android.systemui.volume.dagger.VolumeModule;
+import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubbles;
+
+import java.util.Optional;
import dagger.Binds;
import dagger.BindsOptionalOf;
@@ -126,10 +147,28 @@
@BindsOptionalOf
abstract StatusBar optionalStatusBar();
- @BindsOptionalOf
- abstract Bubbles optionalBubbles();
-
@SysUISingleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
+
+ /** Provides Optional of BubbleManager */
+ @SysUISingleton
+ @Provides
+ static Optional<BubblesManager> provideBubblesManager(Context context,
+ Optional<Bubbles> bubblesOptional,
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarStateController statusBarStateController, ShadeController shadeController,
+ ConfigurationController configurationController,
+ @Nullable IStatusBarService statusBarService, INotificationManager notificationManager,
+ NotificationInterruptStateProvider interruptionStateProvider,
+ ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
+ NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline, SysUiState sysUiState, FeatureFlags featureFlags,
+ DumpManager dumpManager) {
+ return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
+ notificationShadeWindowController, statusBarStateController, shadeController,
+ configurationController, statusBarService, notificationManager,
+ interruptionStateProvider, zenModeController, notifUserManager,
+ groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 6635286..8f3d8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -21,6 +21,7 @@
import com.android.wm.shell.ShellDump;
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -64,13 +65,11 @@
InputConsumerController getInputConsumerController();
// TODO(b/162923491): To be removed once Bubbles migrates over to the Shell
-
@WMSingleton
ShellTaskOrganizer getShellTaskOrganizer();
// TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
// depending on the threading mechanism we go with
-
@WMSingleton
Optional<OneHanded> getOneHanded();
@@ -79,4 +78,7 @@
@WMSingleton
Optional<SplitScreen> getSplitScreen();
+
+ @WMSingleton
+ Optional<Bubbles> getBubbles();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index f07e5af..ebfce66 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -44,6 +44,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -156,7 +157,7 @@
findSensorWithType(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
- authController.isUdfpsEnrolled() /* configured */,
+ authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser()),
DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index cfcceb2..619729e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -57,7 +57,7 @@
protected TextView mDetailDoneButton;
private QSDetailClipper mClipper;
private DetailAdapter mDetailAdapter;
- private QSPanel mQsPanel;
+ private QSPanelController mQsPanelController;
protected View mQsDetailHeader;
protected TextView mQsDetailHeaderTitle;
@@ -114,19 +114,20 @@
public void onClick(View v) {
announceForAccessibility(
mContext.getString(R.string.accessibility_desc_quick_settings));
- mQsPanel.closeDetail();
+ mQsPanelController.closeDetail();
}
};
mDetailDoneButton.setOnClickListener(doneListener);
}
/** */
- public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, QSFooter footer) {
- mQsPanel = panel;
+ public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
+ QSFooter footer) {
+ mQsPanelController = panelController;
mHeader = header;
mFooter = footer;
mHeader.setCallback(mQsPanelCallback);
- mQsPanel.setCallback(mQsPanelCallback);
+ mQsPanelController.setCallback(mQsPanelCallback);
}
public void setHost(QSTileHost host) {
@@ -221,7 +222,7 @@
listener = mTeardownDetailWhenDone;
mHeader.setVisibility(View.VISIBLE);
mFooter.setVisibility(View.VISIBLE);
- mQsPanel.setGridContentVisibility(true);
+ mQsPanelController.setGridContentVisibility(true);
mQsPanelCallback.onScanStateChanged(false);
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -362,7 +363,7 @@
public void onAnimationEnd(Animator animation) {
// Only hide content if still in detail state.
if (mDetailAdapter != null) {
- mQsPanel.setGridContentVisibility(false);
+ mQsPanelController.setGridContentVisibility(false);
mHeader.setVisibility(View.INVISIBLE);
mFooter.setVisibility(View.INVISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 1a7d366..e1bca4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -38,7 +38,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
@@ -69,7 +69,6 @@
private QSAnimator mQSAnimator;
private HeightListener mPanelView;
protected QuickStatusBarHeader mHeader;
- private QSCustomizer mQSCustomizer;
protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
private boolean mListening;
@@ -97,6 +96,7 @@
private float mLastHeaderTranslation;
private QSPanelController mQSPanelController;
private QuickQSPanelController mQuickQSPanelController;
+ private QSCustomizerController mQSCustomizerController;
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -148,16 +148,17 @@
mQSContainerImplController.init();
mContainer = mQSContainerImplController.getView();
- mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, mFooter);
+ mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter);
mQSAnimator = qsFragmentComponent.getQSAnimator();
- mQSCustomizer = view.findViewById(R.id.qs_customize);
- mQSCustomizer.setQs(this);
+ mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
+ mQSCustomizerController.init();
+ mQSCustomizerController.setQs(this);
if (savedInstanceState != null) {
setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
setEditLocation(view);
- mQSCustomizer.restoreInstanceState(savedInstanceState);
+ mQSCustomizerController.restoreInstanceState(savedInstanceState);
if (mQsExpanded) {
mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
}
@@ -181,7 +182,7 @@
if (mListening) {
setListening(false);
}
- mQSCustomizer.setQs(null);
+ mQSCustomizerController.setQs(null);
}
@Override
@@ -189,7 +190,7 @@
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
outState.putBoolean(EXTRA_LISTENING, mListening);
- mQSCustomizer.saveInstanceState(outState);
+ mQSCustomizerController.saveInstanceState(outState);
if (mQsExpanded) {
mQSPanelController.getTileLayout().saveInstanceState(outState);
}
@@ -236,23 +237,22 @@
int[] loc = edit.getLocationOnScreen();
int x = loc[0] + edit.getWidth() / 2;
int y = loc[1] + edit.getHeight() / 2;
- mQSCustomizer.setEditLocation(x, y);
+ mQSCustomizerController.setEditLocation(x, y);
}
@Override
public void setContainer(ViewGroup container) {
if (container instanceof NotificationsQuickSettingsContainer) {
- mQSCustomizer.setContainer((NotificationsQuickSettingsContainer) container);
+ mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
}
}
@Override
public boolean isCustomizing() {
- return mQSCustomizer.isCustomizing();
+ return mQSCustomizerController.isCustomizing();
}
public void setHost(QSTileHost qsh) {
- mQSPanelController.setCustomizer(mQSCustomizer);
mHeader.setQSPanel(mQSPanelController.getView());
mFooter.setQSPanel(mQSPanelController.getView());
mQSDetail.setHost(qsh);
@@ -325,13 +325,9 @@
return mQSPanelController.getView();
}
- public QSCustomizer getCustomizer() {
- return mQSCustomizer;
- }
-
@Override
public boolean isShowingDetail() {
- return mQSPanelController.isShowingCustomize() || mQSDetail.isShowingDetail();
+ return mQSCustomizerController.isCustomizing() || mQSDetail.isShowingDetail();
}
@Override
@@ -553,9 +549,10 @@
public void notifyCustomizeChanged() {
// The customize state changed, so our height changed.
mContainer.updateExpansion();
- mQSPanelScrollView.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE
+ mQSPanelScrollView.setVisibility(!mQSCustomizerController.isCustomizing() ? View.VISIBLE
: View.INVISIBLE);
- mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ mFooter.setVisibility(
+ !mQSCustomizerController.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
mPanelView.onQsHeightChanged();
@@ -567,7 +564,7 @@
*/
@Override
public int getDesiredHeight() {
- if (mQSCustomizer.isCustomizing()) {
+ if (mQSCustomizerController.isCustomizing()) {
return getView().getHeight();
}
if (mQSDetail.isClosingDetail()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 76f2446..758e0c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -46,7 +46,6 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.ToggleSliderView;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -56,7 +55,6 @@
import com.android.systemui.util.animation.DisappearParameters;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
@@ -84,7 +82,6 @@
private final H mHandler = new H();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- private QSTileRevealController mQsTileRevealController;
/** Whether or not the QS media player feature is enabled. */
protected boolean mUsingMediaPlayer;
private int mVisualMarginStart;
@@ -117,7 +114,6 @@
private int mVisualTilePadding;
private boolean mUsingHorizontalLayout;
- private QSCustomizer mCustomizePanel;
private Record mDetailRecord;
private BrightnessMirrorController mBrightnessMirrorController;
@@ -186,10 +182,6 @@
initMediaHostState();
}
- if (mRegularTileLayout instanceof PagedTileLayout) {
- mQsTileRevealController = new QSTileRevealController(mContext, this,
- (PagedTileLayout) mRegularTileLayout);
- }
mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), "");
}
@@ -297,14 +289,6 @@
setMeasuredDimension(getMeasuredWidth(), height);
}
- public QSTileRevealController getQsTileRevealController() {
- return mQsTileRevealController;
- }
-
- public boolean isShowingCustomize() {
- return mCustomizePanel != null && mCustomizePanel.isCustomizing();
- }
-
@Override
protected void onDetachedFromWindow() {
if (mTileLayout != null) {
@@ -362,10 +346,6 @@
mCallback = callback;
}
- void setCustomizer(QSCustomizer customizer) {
- mCustomizePanel = customizer;
- }
-
/**
* Links the footer's page indicator, which is used in landscape orientation to save space.
*
@@ -608,12 +588,6 @@
}
}
- public void onCollapse() {
- if (mCustomizePanel != null && mCustomizePanel.isShown()) {
- mCustomizePanel.hide();
- }
- }
-
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mQSLogger.logPanelExpanded(expanded, getDumpableTag());
@@ -684,10 +658,6 @@
return mExpanded;
}
- void updateRevealedTiles(Collection<QSTile> tiles) {
- mQsTileRevealController.updateRevealedTiles(tiles);
- }
-
void addTile(QSPanelControllerBase.TileRecord tileRecord) {
final QSTile.Callback callback = new QSTile.Callback() {
@Override
@@ -742,29 +712,7 @@
mTileLayout.removeTile(tileRecord);
}
- public void showEdit(final View v) {
- v.post(new Runnable() {
- @Override
- public void run() {
- if (mCustomizePanel != null) {
- if (!mCustomizePanel.isCustomizing()) {
- int[] loc = v.getLocationOnScreen();
- int x = loc[0] + v.getWidth() / 2;
- int y = loc[1] + v.getHeight() / 2;
- mCustomizePanel.show(x, y);
- }
- }
-
- }
- });
- }
-
- public void closeDetail() {
- if (mCustomizePanel != null && mCustomizePanel.isShown()) {
- // Treat this as a detail panel for now, to make things easy.
- mCustomizePanel.hide();
- return;
- }
+ void closeDetail() {
showDetail(false, mDetailRecord);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f222b0d..e9670a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -29,7 +29,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -44,6 +44,7 @@
public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final QSSecurityFooter mQsSecurityFooter;
private final TunerService mTunerService;
+ private final QSCustomizerController mQsCustomizerController;
private final BrightnessController mBrightnessController;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
@@ -61,18 +62,26 @@
@Inject
QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService,
- QSTileHost qstileHost, DumpManager dumpManager,
- MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+ QSTileHost qstileHost, QSCustomizerController qsCustomizerController,
+ QSTileRevealController.Factory qsTileRevealControllerFactory,
+ DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
BrightnessController.Factory brightnessControllerFactory) {
- super(view, qstileHost, metricsLogger, uiEventLogger, dumpManager);
+ super(view, qstileHost, qsCustomizerController, qsTileRevealControllerFactory,
+ metricsLogger, uiEventLogger, dumpManager);
mQsSecurityFooter = qsSecurityFooter;
mTunerService = tunerService;
+ mQsCustomizerController = qsCustomizerController;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessController = brightnessControllerFactory.create(
mView.findViewById(R.id.brightness_slider));
}
@Override
+ public void onInit() {
+ mQsCustomizerController.init();
+ }
+
+ @Override
protected void onViewAttached() {
super.onViewAttached();
mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
@@ -115,16 +124,6 @@
}
/** */
- public void setCustomizer(QSCustomizer customizer) {
- mView.setCustomizer(customizer);
- }
-
- /** */
- public boolean isShowingCustomize() {
- return mView.isShowingCustomize();
- }
-
- /** */
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
}
@@ -148,11 +147,6 @@
}
/** */
- public QSTileRevealController getQsTileRevealController() {
- return mView.getQsTileRevealController();
- }
-
- /** */
public MediaHost getMediaHost() {
return mView.getMediaHost();
}
@@ -196,6 +190,23 @@
/** Start customizing the Quick Settings. */
public void showEdit(View view) {
- mView.showEdit(view);
+ view.post(() -> {
+ if (!mQsCustomizerController.isCustomizing()) {
+ int[] loc = view.getLocationOnScreen();
+ int x = loc[0] + view.getWidth() / 2;
+ int y = loc[1] + view.getHeight() / 2;
+ mQsCustomizerController.show(x, y, false);
+ }
+ });
+ }
+
+ /** */
+ public void setCallback(QSDetail.Callback qsPanelCallback) {
+ mView.setCallback(qsPanelCallback);
+ }
+
+ /** */
+ public void setGridContentVisibility(boolean visible) {
+ mView.setGridContentVisibility(visible);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 68a6cdc..0a4151b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -29,6 +29,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.util.ViewController;
@@ -46,6 +47,8 @@
public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T>
implements Dumpable{
protected final QSTileHost mHost;
+ private final QSCustomizerController mQsCustomizerController;
+ private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final MediaHost mMediaHost;
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
@@ -53,6 +56,7 @@
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mLastOrientation;
+ private QSTileRevealController mQsTileRevealController;
private final QSHost.Callback mQSHostCallback = this::setTiles;
@@ -69,9 +73,13 @@
private String mCachedSpecs = "";
protected QSPanelControllerBase(T view, QSTileHost host,
+ QSCustomizerController qsCustomizerController,
+ QSTileRevealController.Factory qsTileRevealControllerFactory,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
super(view);
mHost = host;
+ mQsCustomizerController = qsCustomizerController;
+ mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mMediaHost = mView.getMediaHost();
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
@@ -80,6 +88,12 @@
@Override
protected void onViewAttached() {
+ QSPanel.QSTileLayout regularTileLayout = mView.createRegularTileLayout();
+ if (regularTileLayout instanceof PagedTileLayout) {
+ mQsTileRevealController = mQsTileRevealControllerFactory.create(
+ (PagedTileLayout) regularTileLayout);
+ }
+
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
mHost.addCallback(mQSHostCallback);
mMediaHost.addVisibilityChangeListener(aBoolean -> {
@@ -111,7 +125,7 @@
/** */
public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
if (!collapsedView) {
- mView.updateRevealedTiles(tiles);
+ mQsTileRevealController.updateRevealedTiles(tiles);
}
for (QSPanelControllerBase.TileRecord record : mRecords) {
mView.removeTile(record);
@@ -192,6 +206,10 @@
/** */
public void closeDetail() {
+ if (mQsCustomizerController.isShown()) {
+ mQsCustomizerController.hide();
+ return;
+ }
mView.closeDetail();
}
@@ -228,6 +246,10 @@
}
}
+ /** */
+ public QSTileRevealController getQsTileRevealController() {
+ return mQsTileRevealController;
+ }
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
index 3d4a417..9414d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java
@@ -8,6 +8,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import java.util.Collection;
@@ -17,13 +18,13 @@
import javax.inject.Inject;
/** */
-@QSScope
public class QSTileRevealController {
private static final long QS_REVEAL_TILES_DELAY = 500L;
private final Context mContext;
private final QSPanel mQSPanel;
private final PagedTileLayout mPagedTileLayout;
+ private final QSCustomizerController mQsCustomizerController;
private final ArraySet<String> mTilesToReveal = new ArraySet<>();
private final Handler mHandler = new Handler();
@@ -38,12 +39,12 @@
});
}
};
-
- @Inject
- QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout) {
+ QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout,
+ QSCustomizerController qsCustomizerController) {
mContext = context;
mQSPanel = qsPanel;
mPagedTileLayout = pagedTileLayout;
+ mQsCustomizerController = qsCustomizerController;
}
public void setExpansion(float expansion) {
@@ -62,7 +63,7 @@
final Set<String> revealedTiles = Prefs.getStringSet(
mContext, QS_TILE_SPECS_REVEALED, Collections.EMPTY_SET);
- if (revealedTiles.isEmpty() || mQSPanel.isShowingCustomize()) {
+ if (revealedTiles.isEmpty() || mQsCustomizerController.isCustomizing()) {
// Do not reveal QS tiles the user has upon first load or those that they directly
// added through customization.
addTileSpecsToRevealed(tileSpecs);
@@ -79,4 +80,24 @@
revealedTiles.addAll(specs);
Prefs.putStringSet(mContext, QS_TILE_SPECS_REVEALED, revealedTiles);
}
+
+ /** TODO(b/168904199): Remove this once QSPanel has its rejection removed. */
+ @QSScope
+ static class Factory {
+ private final Context mContext;
+ private final QSPanel mQsPanel;
+ private final QSCustomizerController mQsCustomizerController;
+
+ @Inject
+ Factory(Context context, QSPanel qsPanel, QSCustomizerController qsCustomizerController) {
+ mContext = context;
+ mQsPanel = qsPanel;
+ mQsCustomizerController = qsCustomizerController;
+ }
+
+ QSTileRevealController create(PagedTileLayout pagedTileLayout) {
+ return new QSTileRevealController(mContext, mQsPanel, pagedTileLayout,
+ mQsCustomizerController);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 97b6e99..a718271 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -23,6 +23,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -41,9 +42,12 @@
@Inject
QuickQSPanelController(QuickQSPanel view, TunerService tunerService, QSTileHost qsTileHost,
+ QSCustomizerController qsCustomizerController,
+ QSTileRevealController.Factory qsTileRevealControllerFactory,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
DumpManager dumpManager) {
- super(view, qsTileHost, metricsLogger, uiEventLogger, dumpManager);
+ super(view, qsTileHost, qsCustomizerController, qsTileRevealControllerFactory,
+ metricsLogger, uiEventLogger, dumpManager);
mTunerService = tunerService;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 8097958..3291aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -20,41 +20,23 @@
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
-import android.os.Bundle;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toolbar;
-import android.widget.Toolbar.OnMenuItemClickListener;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
-import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSDetailClipper;
-import com.android.systemui.qs.QSEditEvent;
-import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateController.Callback;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
/**
* Allows full-screen customization of QS, through show() and hide().
@@ -62,24 +44,16 @@
* This adds itself to the status bar window, so it can appear on top of quick settings and
* *someday* do fancy animations to get into/out of it.
*/
-public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener {
+public class QSCustomizer extends LinearLayout {
- private static final int MENU_RESET = Menu.FIRST;
- private static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
- private static final String TAG = "QSCustomizer";
+ static final int MENU_RESET = Menu.FIRST;
+ static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
private final QSDetailClipper mClipper;
- private final LightBarController mLightBarController;
- private KeyguardStateController mKeyguardStateController;
- private final ScreenLifecycle mScreenLifecycle;
- private final TileQueryHelper mTileQueryHelper;
private final View mTransparentView;
- private final QSTileHost mHost;
private boolean isShown;
- private RecyclerView mRecyclerView;
- private TileAdapter mTileAdapter;
- private Toolbar mToolbar;
+ private final RecyclerView mRecyclerView;
private boolean mCustomizing;
private NotificationsQuickSettingsContainer mNotifQsContainer;
private QS mQs;
@@ -87,90 +61,47 @@
private int mY;
private boolean mOpening;
private boolean mIsShowingNavBackdrop;
- private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
- @Inject
- public QSCustomizer(Context context, AttributeSet attrs,
- LightBarController lightBarController,
- KeyguardStateController keyguardStateController,
- ScreenLifecycle screenLifecycle,
- TileQueryHelper tileQueryHelper,
- QSTileHost qsTileHost,
- UiEventLogger uiEventLogger) {
+ public QSCustomizer(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
mClipper = new QSDetailClipper(findViewById(R.id.customize_container));
- mToolbar = findViewById(com.android.internal.R.id.action_bar);
+ Toolbar toolbar = findViewById(com.android.internal.R.id.action_bar);
TypedValue value = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
- mToolbar.setNavigationIcon(
+ toolbar.setNavigationIcon(
getResources().getDrawable(value.resourceId, mContext.getTheme()));
- mToolbar.setNavigationOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- hide();
- }
- });
- mToolbar.setOnMenuItemClickListener(this);
- mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
+
+ toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
mContext.getString(com.android.internal.R.string.reset));
- mToolbar.setTitle(R.string.qs_edit);
+ toolbar.setTitle(R.string.qs_edit);
mRecyclerView = findViewById(android.R.id.list);
mTransparentView = findViewById(R.id.customizer_transparent_view);
- mTileAdapter = new TileAdapter(getContext(), uiEventLogger);
- mTileQueryHelper = tileQueryHelper;
- mTileQueryHelper.setListener(mTileAdapter);
- mRecyclerView.setAdapter(mTileAdapter);
- mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
- GridLayoutManager layout = new GridLayoutManager(getContext(), 3) {
- @Override
- public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
- RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
- // Do not read row and column every time it changes.
- }
- };
- layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
- mRecyclerView.setLayoutManager(layout);
- mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
- mRecyclerView.addItemDecoration(mTileAdapter.getMarginItemDecoration());
DefaultItemAnimator animator = new DefaultItemAnimator();
animator.setMoveDuration(TileAdapter.MOVE_DURATION);
mRecyclerView.setItemAnimator(animator);
- mLightBarController = lightBarController;
- mKeyguardStateController = keyguardStateController;
- mScreenLifecycle = screenLifecycle;
- mHost = qsTileHost;
- mTileAdapter.setHost(mHost);
- updateNavBackDrop(getResources().getConfiguration());
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateNavBackDrop(newConfig);
- updateResources();
- }
-
- private void updateResources() {
+ void updateResources() {
LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
lp.height = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
mTransparentView.setLayoutParams(lp);
}
- private void updateNavBackDrop(Configuration newConfig) {
+ void updateNavBackDrop(Configuration newConfig, LightBarController lightBarController) {
View navBackdrop = findViewById(R.id.nav_bar_background);
mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600
|| newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE;
if (navBackdrop != null) {
navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE);
}
- updateNavColors();
+ updateNavColors(lightBarController);
}
- private void updateNavColors() {
- mLightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
+ void updateNavColors(LightBarController lightBarController) {
+ lightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
}
public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
@@ -184,39 +115,30 @@
/** Animate and show QSCustomizer panel.
* @param x,y Location on screen of {@code edit} button to determine center of animation.
*/
- public void show(int x, int y) {
+ void show(int x, int y, TileAdapter tileAdapter) {
if (!isShown) {
- int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
+ int[] containerLocation = findViewById(R.id.customize_container).getLocationOnScreen();
mX = x - containerLocation[0];
mY = y - containerLocation[1];
- mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
isShown = true;
mOpening = true;
- setTileSpecs();
setVisibility(View.VISIBLE);
- mClipper.animateCircularClip(mX, mY, true, mExpandAnimationListener);
- queryTiles();
+ mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
mNotifQsContainer.setCustomizerAnimating(true);
mNotifQsContainer.setCustomizerShowing(true);
- mKeyguardStateController.addCallback(mKeyguardCallback);
- updateNavColors();
}
}
- public void showImmediately() {
+ void showImmediately() {
if (!isShown) {
setVisibility(VISIBLE);
mClipper.cancelAnimator();
mClipper.showBackground();
isShown = true;
- setTileSpecs();
setCustomizing(true);
- queryTiles();
mNotifQsContainer.setCustomizerAnimating(false);
mNotifQsContainer.setCustomizerShowing(true);
- mKeyguardStateController.addCallback(mKeyguardCallback);
- updateNavColors();
}
}
@@ -225,9 +147,6 @@
* {@link TileAdapter}.
*/
public void setContentPaddings(int paddingStart, int paddingEnd) {
- int halfMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2;
- mTileAdapter.changeHalfMargin(halfMargin);
mRecyclerView.setPaddingRelative(
paddingStart,
mRecyclerView.getPaddingTop(),
@@ -236,22 +155,14 @@
);
}
- private void queryTiles() {
- mTileQueryHelper.queryTiles(mHost);
- }
-
- public void hide() {
- final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
+ /** Hide the customizer. */
+ public void hide(boolean animate) {
if (isShown) {
- mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
isShown = false;
- mToolbar.dismissPopupMenus();
mClipper.cancelAnimator();
// Make sure we're not opening (because we're closing). Nobody can think we are
// customizing after the next two lines.
mOpening = false;
- setCustomizing(false);
- save();
if (animate) {
mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
} else {
@@ -259,8 +170,6 @@
}
mNotifQsContainer.setCustomizerAnimating(animate);
mNotifQsContainer.setCustomizerShowing(false);
- mKeyguardStateController.removeCallback(mKeyguardCallback);
- updateNavColors();
}
}
@@ -268,7 +177,7 @@
return isShown;
}
- private void setCustomizing(boolean customizing) {
+ void setCustomizing(boolean customizing) {
mCustomizing = customizing;
mQs.notifyCustomizeChanged();
}
@@ -277,78 +186,21 @@
return mCustomizing || mOpening;
}
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_RESET:
- mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
- reset();
- break;
- }
- return false;
- }
-
- private void reset() {
- mTileAdapter.resetTileSpecs(mHost, QSTileHost.getDefaultSpecs(mContext));
- }
-
- private void setTileSpecs() {
- List<String> specs = new ArrayList<>();
- for (QSTile tile : mHost.getTiles()) {
- specs.add(tile.getTileSpec());
- }
- mTileAdapter.setTileSpecs(specs);
- mRecyclerView.setAdapter(mTileAdapter);
- }
-
- private void save() {
- if (mTileQueryHelper.isFinished()) {
- mTileAdapter.saveSpecs(mHost);
- }
- }
-
-
- public void saveInstanceState(Bundle outState) {
- if (isShown) {
- mKeyguardStateController.removeCallback(mKeyguardCallback);
- }
- outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing);
- }
-
- public void restoreInstanceState(Bundle savedInstanceState) {
- boolean customizing = savedInstanceState.getBoolean(EXTRA_QS_CUSTOMIZING);
- if (customizing) {
- setVisibility(VISIBLE);
- addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- removeOnLayoutChangeListener(this);
- showImmediately();
- }
- });
- }
- }
/** @param x,y Location on screen of animation center.
*/
public void setEditLocation(int x, int y) {
- int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
+ int[] containerLocation = findViewById(R.id.customize_container).getLocationOnScreen();
mX = x - containerLocation[0];
mY = y - containerLocation[1];
}
- private final Callback mKeyguardCallback = new Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- if (!isAttachedToWindow()) return;
- if (mKeyguardStateController.isShowing() && !mOpening) {
- hide();
- }
- }
- };
+ class ExpandAnimatorListener extends AnimatorListenerAdapter {
+ private final TileAdapter mTileAdapter;
- private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
+ ExpandAnimatorListener(TileAdapter tileAdapter) {
+ mTileAdapter = tileAdapter;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
if (isShown) {
@@ -356,6 +208,7 @@
}
mOpening = false;
mNotifQsContainer.setCustomizerAnimating(false);
+ mRecyclerView.setAdapter(mTileAdapter);
}
@Override
@@ -363,7 +216,7 @@
mOpening = false;
mNotifQsContainer.setCustomizerAnimating(false);
}
- };
+ }
private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
@Override
@@ -372,7 +225,6 @@
setVisibility(View.GONE);
}
mNotifQsContainer.setCustomizerAnimating(false);
- mRecyclerView.setAdapter(mTileAdapter);
}
@Override
@@ -383,4 +235,12 @@
mNotifQsContainer.setCustomizerAnimating(false);
}
};
-}
+
+ public RecyclerView getRecyclerView() {
+ return mRecyclerView;
+ }
+
+ public boolean isOpening() {
+ return mOpening;
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
new file mode 100644
index 0000000..9f4c58b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -0,0 +1,247 @@
+/*
+ * 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.qs.customize;
+
+import static com.android.systemui.qs.customize.QSCustomizer.EXTRA_QS_CUSTOMIZING;
+import static com.android.systemui.qs.customize.QSCustomizer.MENU_RESET;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toolbar;
+import android.widget.Toolbar.OnMenuItemClickListener;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.QSEditEvent;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/** {@link ViewController} for {@link QSCustomizer}. */
+@QSScope
+public class QSCustomizerController extends ViewController<QSCustomizer> {
+ private final TileQueryHelper mTileQueryHelper;
+ private final QSTileHost mQsTileHost;
+ private final TileAdapter mTileAdapter;
+ private final ScreenLifecycle mScreenLifecycle;
+ private final KeyguardStateController mKeyguardStateController;
+ private final LightBarController mLightBarController;
+ private final ConfigurationController mConfigurationController;
+ private final UiEventLogger mUiEventLogger;
+ private final Toolbar mToolbar;
+
+ private final OnMenuItemClickListener mOnMenuItemClickListener = new OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getItemId() == MENU_RESET) {
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
+ reset();
+ }
+ return false;
+ }
+ };
+
+ private final KeyguardStateController.Callback mKeyguardCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (!mView.isAttachedToWindow()) return;
+ if (mKeyguardStateController.isShowing() && !mView.isOpening()) {
+ hide();
+ }
+ }
+ };
+
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mView.updateNavBackDrop(newConfig, mLightBarController);
+ mView.updateResources();
+ }
+ };
+
+ @Inject
+ protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
+ QSTileHost qsTileHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
+ KeyguardStateController keyguardStateController, LightBarController lightBarController,
+ ConfigurationController configurationController, UiEventLogger uiEventLogger) {
+ super(view);
+ mTileQueryHelper = tileQueryHelper;
+ mQsTileHost = qsTileHost;
+ mTileAdapter = tileAdapter;
+ mScreenLifecycle = screenLifecycle;
+ mKeyguardStateController = keyguardStateController;
+ mLightBarController = lightBarController;
+ mConfigurationController = configurationController;
+ mUiEventLogger = uiEventLogger;
+
+ mToolbar = mView.findViewById(com.android.internal.R.id.action_bar);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mView.updateNavBackDrop(getResources().getConfiguration(), mLightBarController);
+
+ mConfigurationController.addCallback(mConfigurationListener);
+
+ mTileQueryHelper.setListener(mTileAdapter);
+ int halfMargin =
+ getResources().getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2;
+ mTileAdapter.changeHalfMargin(halfMargin);
+
+ RecyclerView recyclerView = mView.getRecyclerView();
+ recyclerView.setAdapter(mTileAdapter);
+ mTileAdapter.getItemTouchHelper().attachToRecyclerView(recyclerView);
+ GridLayoutManager layout = new GridLayoutManager(getContext(), 3) {
+ @Override
+ public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+ RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
+ // Do not read row and column every time it changes.
+ }
+ };
+ layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
+ recyclerView.setLayoutManager(layout);
+ recyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
+ recyclerView.addItemDecoration(mTileAdapter.getMarginItemDecoration());
+
+ mToolbar.setOnMenuItemClickListener(mOnMenuItemClickListener);
+ mToolbar.setNavigationOnClickListener(v -> hide());
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mTileQueryHelper.setListener(null);
+ mToolbar.setOnMenuItemClickListener(null);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ }
+
+
+ private void reset() {
+ mTileAdapter.resetTileSpecs(QSTileHost.getDefaultSpecs(getContext()));
+ }
+
+ public boolean isCustomizing() {
+ return mView.isCustomizing();
+ }
+
+ /** */
+ public void show(int x, int y, boolean immediate) {
+ if (!mView.isShown()) {
+ setTileSpecs();
+ if (immediate) {
+ mView.showImmediately();
+ } else {
+ mView.show(x, y, mTileAdapter);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
+ }
+ mTileQueryHelper.queryTiles(mQsTileHost);
+ mKeyguardStateController.addCallback(mKeyguardCallback);
+ mView.updateNavColors(mLightBarController);
+ }
+ }
+
+ /** */
+ public void setQs(QSFragment qsFragment) {
+ mView.setQs(qsFragment);
+ }
+
+ /** */
+ public void restoreInstanceState(Bundle savedInstanceState) {
+ boolean customizing = savedInstanceState.getBoolean(EXTRA_QS_CUSTOMIZING);
+ if (customizing) {
+ mView.setVisibility(View.VISIBLE);
+ mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ mView.removeOnLayoutChangeListener(this);
+ show(0, 0, true);
+ }
+ });
+ }
+ }
+
+ /** */
+ public void saveInstanceState(Bundle outState) {
+ if (mView.isShown()) {
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
+ }
+ outState.putBoolean(EXTRA_QS_CUSTOMIZING, mView.isCustomizing());
+ }
+
+ /** */
+ public void setEditLocation(int x, int y) {
+ mView.setEditLocation(x, y);
+ }
+
+ /** */
+ public void setContainer(NotificationsQuickSettingsContainer container) {
+ mView.setContainer(container);
+ }
+
+ public boolean isShown() {
+ return mView.isShown();
+ }
+
+ /** Hice the customizer. */
+ public void hide() {
+ final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
+ if (mView.isShown()) {
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
+ mToolbar.dismissPopupMenus();
+ mView.setCustomizing(false);
+ save();
+ mView.hide(animate);
+ mView.updateNavColors(mLightBarController);
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
+ }
+ }
+
+ private void save() {
+ if (mTileQueryHelper.isFinished()) {
+ mTileAdapter.saveSpecs(mQsTileHost);
+ }
+ }
+
+ private void setTileSpecs() {
+ List<String> specs = new ArrayList<>();
+ for (QSTile tile : mQsTileHost.getTiles()) {
+ specs.add(tile.getTileSpec());
+ }
+ mTileAdapter.setTileSpecs(specs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index b471dfa..dfc771b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -46,12 +46,17 @@
import com.android.systemui.qs.customize.TileAdapter.Holder;
import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
+import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
+/** */
+@QSScope
public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
private static final long DRAG_LENGTH = 100;
private static final float DRAG_SCALE = 1.2f;
@@ -78,6 +83,7 @@
private final ItemDecoration mDecoration;
private final MarginTileDecoration mMarginDecoration;
private final int mMinNumTiles;
+ private final QSTileHost mHost;
private int mEditIndex;
private int mTileDividerIndex;
private int mFocusIndex;
@@ -89,13 +95,14 @@
private Holder mCurrentDrag;
private int mAccessibilityAction = ACTION_NONE;
private int mAccessibilityFromIndex;
- private QSTileHost mHost;
private final UiEventLogger mUiEventLogger;
private final AccessibilityDelegateCompat mAccessibilityDelegate;
private RecyclerView mRecyclerView;
- public TileAdapter(Context context, UiEventLogger uiEventLogger) {
+ @Inject
+ public TileAdapter(Context context, QSTileHost qsHost, UiEventLogger uiEventLogger) {
mContext = context;
+ mHost = qsHost;
mUiEventLogger = uiEventLogger;
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);
@@ -114,10 +121,6 @@
mRecyclerView = null;
}
- public void setHost(QSTileHost host) {
- mHost = host;
- }
-
public ItemTouchHelper getItemTouchHelper() {
return mItemTouchHelper;
}
@@ -154,9 +157,10 @@
mAccessibilityAction = ACTION_NONE;
}
- public void resetTileSpecs(QSTileHost host, List<String> specs) {
+ /** */
+ public void resetTileSpecs(List<String> specs) {
// Notify the host so the tiles get removed callbacks.
- host.changeTiles(mCurrentSpecs, specs);
+ mHost.changeTiles(mCurrentSpecs, specs);
setTileSpecs(specs);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index b795a5f..59490c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -37,6 +37,7 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
import com.android.systemui.settings.UserTracker;
@@ -50,6 +51,8 @@
import javax.inject.Inject;
+/** */
+@QSScope
public class TileQueryHelper {
private static final String TAG = "TileQueryHelper";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 51b2c8d..8cc0502 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -22,6 +22,7 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.qs.QuickQSPanelController;
+import com.android.systemui.qs.customize.QSCustomizerController;
import dagger.BindsInstance;
import dagger.Subcomponent;
@@ -32,6 +33,7 @@
@Subcomponent(modules = {QSFragmentModule.class})
@QSScope
public interface QSFragmentComponent {
+
/** Factory for building a {@link QSFragmentComponent}. */
@Subcomponent.Factory
interface Factory {
@@ -52,4 +54,7 @@
/** Construct a {@link QSFooter} */
QSFooter getQSFooter();
+
+ /** Construct a {@link QSCustomizerController}. */
+ QSCustomizerController getQSCustomizerController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 4bf4eff..354b2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.customize.QSCustomizer;
import dagger.Binds;
import dagger.Module;
@@ -87,4 +88,11 @@
qsFooterViewController.init();
return qsFooterViewController;
}
+
+ /** */
+ @Provides
+ @QSScope
+ static QSCustomizer providesQSCutomizer(@RootView View view) {
+ return view.findViewById(R.id.qs_customize);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 260f557..0dde931 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -27,7 +27,6 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Notification;
-import android.app.WindowContext;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
@@ -43,6 +42,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -58,8 +58,10 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.List;
import java.util.function.Consumer;
@@ -143,6 +145,8 @@
private final DisplayMetrics mDisplayMetrics;
private final AccessibilityManager mAccessibilityManager;
private final MediaActionSound mCameraSound;
+ private final ScrollCaptureClient mScrollCaptureClient;
+ private final DeviceConfigProxy mConfigProxy;
private final Binder mWindowToken;
private ScreenshotView mScreenshotView;
@@ -173,11 +177,16 @@
};
@Inject
- ScreenshotController(Context context, ScreenshotSmartActions screenshotSmartActions,
+ ScreenshotController(
+ Context context,
+ ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController screenshotNotificationsController,
- UiEventLogger uiEventLogger) {
+ ScrollCaptureClient scrollCaptureClient,
+ UiEventLogger uiEventLogger,
+ DeviceConfigProxy configProxy) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
+ mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
@@ -186,6 +195,7 @@
mWindowManager = mContext.getSystemService(WindowManager.class);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+ mConfigProxy = configProxy;
reloadAssets();
Configuration config = mContext.getResources().getConfiguration();
@@ -193,6 +203,7 @@
mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT;
mWindowToken = new Binder("ScreenshotController");
+ mScrollCaptureClient.setHostWindowToken(mWindowToken);
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -455,6 +466,19 @@
// Start the post-screenshot animation
startAnimation(finisher, screenRect, screenInsets, showFlash);
+
+ if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
+ mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
+ mScreenshotView.showScrollChip(() ->
+ runScrollCapture(connection,
+ () -> dismissScreenshot(false))));
+ }
+ }
+
+ private void runScrollCapture(ScrollCaptureClient.Connection connection,
+ Runnable after) {
+ new ScrollCaptureController(mContext, connection).run(after);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 29f6e8b..3383f80 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -113,6 +113,7 @@
private FrameLayout mDismissButton;
private ScreenshotActionChip mShareChip;
private ScreenshotActionChip mEditChip;
+ private ScreenshotActionChip mScrollChip;
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
@@ -152,6 +153,20 @@
mContext.getDisplay().getRealMetrics(mDisplayMetrics);
}
+ /**
+ * Called to display the scroll action chip when support is detected.
+ *
+ * @param onClick the action to take when the chip is clicked.
+ */
+ public void showScrollChip(Runnable onClick) {
+ mScrollChip.setVisibility(VISIBLE);
+ mScrollChip.setOnClickListener((v) ->
+ onClick.run()
+ // TODO Logging, store event consumer to a field
+ //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
+ );
+ }
+
@Override // ViewTreeObserver.OnComputeInternalInsetsListener
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
@@ -193,6 +208,7 @@
mScreenshotSelectorView = requireNonNull(findViewById(R.id.global_screenshot_selector));
mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip));
+ mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip));
mScreenshotPreview.setClipToOutline(true);
mScreenshotPreview.setOutlineProvider(new ViewOutlineProvider() {
@@ -387,7 +403,7 @@
});
chips.add(mShareChip);
- mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit));
+ mEditChip.setText(mContext.getString(R.string.screenshot_edit_label));
mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
mEditChip.setOnClickListener(v -> {
mEditChip.setIsPending(true);
@@ -402,6 +418,11 @@
mPendingInteraction = PendingInteraction.PREVIEW;
});
+ mScrollChip.setText(mContext.getString(R.string.screenshot_scroll_label));
+ mScrollChip.setIcon(Icon.createWithResource(mContext,
+ R.drawable.ic_screenshot_scroll), true);
+ chips.add(mScrollChip);
+
// remove the margin from the last chip so that it's correctly aligned with the end
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
mActionsView.getChildAt(0).getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
new file mode 100644
index 0000000..ea835fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -0,0 +1,346 @@
+/*
+ * 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.screenshot;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.UiContext;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureConnection;
+import android.view.IWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.view.ScrollCaptureViewSupport;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * High level interface to scroll capture API.
+ */
+public class ScrollCaptureClient {
+
+ @VisibleForTesting
+ static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
+
+ private static final String TAG = "ScrollCaptureClient";
+
+ /** Whether to log method names and arguments for most calls */
+ private static final boolean DEBUG_TRACE = false;
+
+ /**
+ * A connection to a remote window. Starts a capture session.
+ */
+ public interface Connection {
+ /**
+ * Session start should be deferred until UI is active because of resource allocation and
+ * potential visible side effects in the target window.
+ *
+ * @param maxBuffers the maximum number of buffers (tiles) that may be in use at one
+ * time, tiles are not cached anywhere so set this to a large enough
+ * number to retain offscreen content until it is no longer needed
+ * @param sessionConsumer listener to receive the session once active
+ */
+ void start(int maxBuffers, Consumer<Session> sessionConsumer);
+
+ /**
+ * Close the connection.
+ */
+ void close();
+ }
+
+ static class CaptureResult {
+ public final Image image;
+ /**
+ * The area requested, in content rect space, relative to scroll-bounds.
+ */
+ public final Rect requested;
+ /**
+ * The actual area captured, in content rect space, relative to scroll-bounds. This may be
+ * cropped or empty depending on available content.
+ */
+ public final Rect captured;
+
+ // Error?
+
+ private CaptureResult(Image image, Rect request, Rect captured) {
+ this.image = image;
+ this.requested = request;
+ this.captured = captured;
+ }
+ }
+
+ /**
+ * Represents the connection to a target window and provides a mechanism for requesting tiles.
+ */
+ interface Session {
+ /**
+ * Request the given horizontal strip. Values are y-coordinates in captured space, relative
+ * to start position.
+ *
+ * @param contentRect the area to capture, in content rect space, relative to scroll-bounds
+ * @param consumer listener to be informed of the result
+ */
+ void requestTile(Rect contentRect, Consumer<CaptureResult> consumer);
+
+ /**
+ * End the capture session, return the target app to original state. The returned
+ * stage must be waited for to complete to allow the target app a chance to restore to
+ * original state before becoming visible.
+ *
+ * @return a stage presenting the session shutdown
+ */
+ void end(Runnable listener);
+
+ int getMaxTileHeight();
+
+ int getMaxTileWidth();
+ }
+
+ private final IWindowManager mWindowManagerService;
+ private IBinder mHostWindowToken;
+
+ @Inject
+ public ScrollCaptureClient(@UiContext Context context, IWindowManager windowManagerService) {
+ requireNonNull(context.getDisplay(), "context must be associated with a Display!");
+ mWindowManagerService = windowManagerService;
+ }
+
+ public void setHostWindowToken(IBinder token) {
+ mHostWindowToken = token;
+ }
+
+ /**
+ * Check for scroll capture support.
+ *
+ * @param displayId id for the display containing the target window
+ * @param consumer receives a connection when available
+ */
+ public void request(int displayId, Consumer<Connection> consumer) {
+ request(displayId, MATCH_ANY_TASK, consumer);
+ }
+
+ /**
+ * Check for scroll capture support.
+ *
+ * @param displayId id for the display containing the target window
+ * @param taskId id for the task containing the target window or {@link #MATCH_ANY_TASK}.
+ * @param consumer receives a connection when available
+ */
+ public void request(int displayId, int taskId, Consumer<Connection> consumer) {
+ try {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken
+ + ", taskId=" + taskId + ", consumer=" + consumer + ")");
+ }
+ mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
+ new ControllerCallbacks(consumer));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Ignored remote exception", e);
+ }
+ }
+
+ private static class ControllerCallbacks extends IScrollCaptureCallbacks.Stub implements
+ Connection, Session, IBinder.DeathRecipient {
+
+ private IScrollCaptureConnection mConnection;
+ private Consumer<Connection> mConnectionConsumer;
+ private Consumer<Session> mSessionConsumer;
+ private Consumer<CaptureResult> mResultConsumer;
+ private Runnable mShutdownListener;
+
+ private ImageReader mReader;
+ private Rect mScrollBounds;
+ private Rect mRequestRect;
+ private boolean mStarted;
+
+ private ControllerCallbacks(Consumer<Connection> connectionConsumer) {
+ mConnectionConsumer = connectionConsumer;
+ }
+
+ // IScrollCaptureCallbacks
+
+ @Override
+ public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds,
+ Point positionInWindow) throws RemoteException {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds
+ + ", positionInWindow=" + positionInWindow + ")");
+ }
+ mConnection = connection;
+ mConnection.asBinder().linkToDeath(this, 0);
+ mScrollBounds = scrollBounds;
+ mConnectionConsumer.accept(this);
+ mConnectionConsumer = null;
+ }
+
+ @Override
+ public void onUnavailable() throws RemoteException {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "onUnavailable");
+ }
+ // The targeted app does not support scroll capture
+ // or the window could not be found... etc etc.
+ }
+
+ @Override
+ public void onCaptureStarted() {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "onCaptureStarted()");
+ }
+ mSessionConsumer.accept(this);
+ mSessionConsumer = null;
+ }
+
+ @Override
+ public void onCaptureBufferSent(long frameNumber, Rect contentArea) {
+ Image image = null;
+ if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) {
+ image = mReader.acquireNextImage();
+ }
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber
+ + ", contentArea=" + contentArea + ") image=" + image);
+ }
+ // Save and clear first, since the consumer will likely request the next
+ // tile, otherwise the new consumer will be wiped out.
+ Consumer<CaptureResult> consumer = mResultConsumer;
+ mResultConsumer = null;
+ consumer.accept(new CaptureResult(image, mRequestRect, contentArea));
+ }
+
+ @Override
+ public void onConnectionClosed() {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "onConnectionClosed()");
+ }
+ disconnect();
+ if (mShutdownListener != null) {
+ mShutdownListener.run();
+ mShutdownListener = null;
+ }
+ }
+
+ // Misc
+
+ private void disconnect() {
+ if (mConnection != null) {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+ mConnection = null;
+ }
+
+ // ScrollCaptureController.Connection
+
+ // -> Error handling: BiConsumer<Session, Throwable> ?
+ @Override
+ public void start(int maxBufferCount, Consumer<Session> sessionConsumer) {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "start(maxBufferCount=" + maxBufferCount
+ + ", sessionConsumer=" + sessionConsumer + ")");
+ }
+ mReader = ImageReader.newInstance(mScrollBounds.width(), mScrollBounds.height(),
+ PixelFormat.RGBA_8888, maxBufferCount, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ mSessionConsumer = sessionConsumer;
+ try {
+ mConnection.startCapture(mReader.getSurface());
+ mStarted = true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "should not be happening :-(");
+ // ?
+ //mSessionListener.onError(e);
+ //mSessionListener = null;
+ }
+ }
+
+ @Override
+ public void close() {
+ end(null);
+ }
+
+ // ScrollCaptureController.Session
+
+ @Override
+ public void end(Runnable listener) {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "end(listener=" + listener + ")");
+ }
+ if (mStarted) {
+ mShutdownListener = listener;
+ try {
+ // listener called from onConnectionClosed callback
+ mConnection.endCapture();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Ignored exception from endCapture()", e);
+ disconnect();
+ listener.run();
+ }
+ } else {
+ disconnect();
+ listener.run();
+ }
+ }
+
+ @Override
+ public int getMaxTileHeight() {
+ return mScrollBounds.height();
+ }
+
+ @Override
+ public int getMaxTileWidth() {
+ return mScrollBounds.width();
+ }
+
+ @Override
+ public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")");
+ }
+ mRequestRect = new Rect(contentRect);
+ mResultConsumer = consumer;
+ try {
+ mConnection.requestImage(mRequestRect);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught remote exception from requestImage", e);
+ // ?
+ }
+ }
+
+ /**
+ * The process hosting the window went away abruptly!
+ */
+ @Override
+ public void binderDied() {
+ if (DEBUG_TRACE) {
+ Log.d(TAG, "binderDied()");
+ }
+ disconnect();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 5ced40c..800d679 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,46 +16,231 @@
package com.android.systemui.screenshot;
-import android.os.IBinder;
-import android.view.IWindowManager;
+import static android.graphics.ColorSpace.Named.SRGB;
-import javax.inject.Inject;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorSpace;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.media.ExifInterface;
+import android.media.Image;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
+import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Consumer;
/**
- * Stub
+ * Interaction controller between the UI and ScrollCaptureClient.
*/
public class ScrollCaptureController {
+ private static final String TAG = "ScrollCaptureController";
- public static final int STATUS_A = 0;
- public static final int STATUS_B = 1;
+ public static final int MAX_PAGES = 5;
+ public static final int MAX_HEIGHT = 12000;
- private final IWindowManager mWindowManagerService;
- private StatusListener mListener;
+ private final Connection mConnection;
+ private final Context mContext;
+ private Picture mPicture;
- /**
- *
- * @param windowManagerService
- */
- @Inject
- public ScrollCaptureController(IWindowManager windowManagerService) {
- mWindowManagerService = windowManagerService;
- }
-
- interface StatusListener {
- void onScrollCaptureStatus(boolean available);
+ public ScrollCaptureController(Context context, Connection connection) {
+ mContext = context;
+ mConnection = connection;
}
/**
+ * Run scroll capture!
*
- * @param window
- * @param listener
+ * @param after action to take after the flow is complete
*/
- public void getStatus(IBinder window, StatusListener listener) {
- mListener = listener;
-// try {
-// mWindowManagerService.requestScrollCapture(window, new ClientCallbacks());
-// } catch (RemoteException e) {
-// }
+ public void run(final Runnable after) {
+ mConnection.start(MAX_PAGES, (session) -> startCapture(session, after));
}
+ private void startCapture(Session session, final Runnable after) {
+ Rect requestRect = new Rect(0, 0,
+ session.getMaxTileWidth(), session.getMaxTileHeight());
+ Consumer<ScrollCaptureClient.CaptureResult> consumer =
+ new Consumer<ScrollCaptureClient.CaptureResult>() {
+
+ int mFrameCount = 0;
+
+ @Override
+ public void accept(ScrollCaptureClient.CaptureResult result) {
+ mFrameCount++;
+ boolean emptyFrame = result.captured.height() == 0;
+ if (!emptyFrame) {
+ mPicture = stackBelow(mPicture, result.image, result.captured.width(),
+ result.captured.height());
+ }
+ if (emptyFrame || mFrameCount > MAX_PAGES
+ || requestRect.bottom > MAX_HEIGHT) {
+ Uri uri = null;
+ if (mPicture != null) {
+ // This is probably on a binder thread right now ¯\_(ツ)_/¯
+ uri = writeImage(Bitmap.createBitmap(mPicture));
+ // Release those buffers!
+ mPicture.close();
+ }
+ if (uri != null) {
+ launchViewer(uri);
+ } else {
+ Toast.makeText(mContext, "Failed to create tall screenshot",
+ Toast.LENGTH_SHORT).show();
+ }
+ session.end(after); // end session, close connection, after.run()
+ return;
+ }
+ requestRect.offset(0, session.getMaxTileHeight());
+ session.requestTile(requestRect, /* consumer */ this);
+ }
+ };
+
+ // fire it up!
+ session.requestTile(requestRect, consumer);
+ };
+
+
+ /**
+ * Combine the top {@link Picture} with an {@link Image} by appending the image directly
+ * below, creating a result that is the combined height of both.
+ * <p>
+ * Note: no pixel data is transferred here, only a record of drawing commands. Backing
+ * hardware buffers must not be modified/recycled until the picture is
+ * {@link Picture#close closed}.
+ *
+ * @param top the existing picture
+ * @param below the image to append below
+ * @param cropWidth the width of the pixel data to use from the image
+ * @param cropHeight the height of the pixel data to use from the image
+ *
+ * @return a new Picture which draws the previous picture with the image below it
+ */
+ private static Picture stackBelow(Picture top, Image below, int cropWidth, int cropHeight) {
+ int width = cropWidth;
+ int height = cropHeight;
+ if (top != null) {
+ height += top.getHeight();
+ width = Math.max(width, top.getWidth());
+ }
+ Picture combined = new Picture();
+ Canvas canvas = combined.beginRecording(width, height);
+ int y = 0;
+ if (top != null) {
+ canvas.drawPicture(top, new Rect(0, 0, top.getWidth(), top.getHeight()));
+ y += top.getHeight();
+ }
+ canvas.drawBitmap(Bitmap.wrapHardwareBuffer(
+ below.getHardwareBuffer(), ColorSpace.get(SRGB)), 0, y, null);
+ combined.endRecording();
+ return combined;
+ }
+
+ Uri writeImage(Bitmap image) {
+ ContentResolver resolver = mContext.getContentResolver();
+ long mImageTime = System.currentTimeMillis();
+ String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
+ String mImageFileName = String.format("tall_Screenshot_%s.png", imageDate);
+ String mScreenshotId = String.format("Screenshot_%s", UUID.randomUUID());
+ try {
+ // Save the screenshot to the MediaStore
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
+ values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
+ values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
+ values.put(
+ MediaStore.MediaColumns.DATE_EXPIRES,
+ (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
+ values.put(MediaStore.MediaColumns.IS_PENDING, 1);
+
+ final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ values);
+ try {
+ try (OutputStream out = resolver.openOutputStream(uri)) {
+ if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
+ throw new IOException("Failed to compress");
+ }
+ }
+
+ // Next, write metadata to help index the screenshot
+ try (ParcelFileDescriptor pfd = resolver.openFile(uri, "rw", null)) {
+ final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
+
+ exif.setAttribute(ExifInterface.TAG_SOFTWARE,
+ "Android " + Build.DISPLAY);
+
+ exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
+ Integer.toString(image.getWidth()));
+ exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
+ Integer.toString(image.getHeight()));
+
+ final ZonedDateTime time = ZonedDateTime.ofInstant(
+ Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
+ exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
+ exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("SSS").format(time));
+
+ if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
+ } else {
+ exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
+ DateTimeFormatter.ofPattern("XXX").format(time));
+ }
+ exif.saveAttributes();
+ }
+
+ // Everything went well above, publish it!
+ values.clear();
+ values.put(MediaStore.MediaColumns.IS_PENDING, 0);
+ values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
+ resolver.update(uri, values, null, null);
+ return uri;
+ } catch (Exception e) {
+ resolver.delete(uri, null);
+ throw e;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "unable to save screenshot", e);
+ }
+ return null;
+ }
+
+ void launchViewer(Uri uri) {
+ Intent editIntent = new Intent(Intent.ACTION_VIEW);
+ editIntent.setType("image/png");
+ editIntent.setData(uri);
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivityAsUser(editIntent, UserHandle.CURRENT);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index a92b9e4..9bd34ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -26,7 +26,6 @@
import android.view.ViewGroup;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
@@ -43,6 +42,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.Assert;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index cee9c70..efd0519 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -21,7 +21,6 @@
import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
@@ -62,6 +61,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 382715a..45e8098 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -32,6 +34,7 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -226,10 +229,27 @@
}
});
anim.addListener(new AnimatorListenerAdapter() {
+ private boolean mWasCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_APP_START);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mWasCancelled = true;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
setExpandAnimationRunning(false);
invokeCallback(iRemoteAnimationFinishedCallback);
+ if (!mWasCancelled) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_APP_START);
+ } else {
+ InteractionJankMonitor.getInstance().cancel(CUJ_NOTIFICATION_APP_START);
+ }
}
});
anim.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index eee9cc6..967524c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -16,9 +16,7 @@
package com.android.systemui.statusbar.notification;
-import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
-import android.util.Log;
import android.util.Property;
import android.view.View;
@@ -34,9 +32,14 @@
public static final AnimatableProperty X = AnimatableProperty.from(View.X,
R.id.x_animator_tag, R.id.x_animator_tag_start_value, R.id.x_animator_tag_end_value);
+
public static final AnimatableProperty Y = AnimatableProperty.from(View.Y,
R.id.y_animator_tag, R.id.y_animator_tag_start_value, R.id.y_animator_tag_end_value);
+ public static final AnimatableProperty TRANSLATION_X = AnimatableProperty.from(
+ View.TRANSLATION_X, R.id.x_animator_tag, R.id.x_animator_tag_start_value,
+ R.id.x_animator_tag_end_value);
+
/**
* Similar to X, however this doesn't allow for any other modifications other than from this
* property. When using X, it's possible that the view is laid out during the animation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 7d8979c..aef01e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -22,10 +22,10 @@
import android.view.View;
import com.android.systemui.DejankUtils;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 83a569b..29a030f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -26,6 +24,8 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.HashSet;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index 36adfac..3db5440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -22,7 +22,6 @@
import android.util.Log;
import com.android.systemui.Dumpable;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -34,6 +33,7 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.wm.shell.bubbles.Bubbles;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index 049b471..52b9b06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
-import com.android.systemui.bubbles.Bubbles
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.StatusBar
+import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.Optional
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 45a5d10..8f352ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
-import com.android.systemui.bubbles.Bubbles
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.FeatureFlags
@@ -41,6 +40,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.RemoteInputUriController
+import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.io.FileDescriptor
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 7569c1b..d0e68bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.init
import android.service.notification.StatusBarNotification
-import com.android.systemui.bubbles.Bubbles
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
@@ -25,6 +24,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.StatusBar
+import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.Optional
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 094e866..10273cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -33,6 +33,7 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -750,12 +751,16 @@
if (!mWasCancelled) {
enableAppearDrawing(false);
onAppearAnimationFinished(isAppearing);
+ InteractionJankMonitor.getInstance().end(getCujType(isAppearing));
+ } else {
+ InteractionJankMonitor.getInstance().cancel(getCujType(isAppearing));
}
}
@Override
public void onAnimationStart(Animator animation) {
mWasCancelled = false;
+ InteractionJankMonitor.getInstance().begin(getCujType(isAppearing));
}
@Override
@@ -766,6 +771,18 @@
mAppearAnimator.start();
}
+ private int getCujType(boolean isAppearing) {
+ if (mIsHeadsUpAnimation) {
+ return isAppearing
+ ? InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR
+ : InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+ } else {
+ return isAppearing
+ ? InteractionJankMonitor.CUJ_NOTIFICATION_ADD
+ : InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
+ }
+ }
+
protected void onAppearAnimationFinished(boolean wasAppearing) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 280c525..a011d36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Path;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
@@ -43,6 +44,7 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -327,6 +329,7 @@
private boolean mShelfIconVisible;
private boolean mAboveShelf;
private OnUserInteractionCallback mOnUserInteractionCallback;
+ private NotificationGutsManager mNotificationGutsManager;
private boolean mIsLowPriority;
private boolean mIsColorized;
private boolean mUseIncreasedCollapsedHeight;
@@ -1089,6 +1092,13 @@
};
}
+ /** The click listener for the snooze button. */
+ public View.OnClickListener getSnoozeClickListener(MenuItem item) {
+ return v -> {
+ mNotificationGutsManager.openGuts(this, 0, 0, item);
+ };
+ }
+
private void updateClickAndFocus() {
boolean normalChild = !isChildInGroup() || isGroupExpanded();
boolean clickable = mOnClickListener != null && normalChild;
@@ -1155,10 +1165,11 @@
*/
@Nullable
public NotificationMenuRowPlugin createMenu() {
- if (mMenuRow == null) {
+ final boolean removeShelf = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0 /* show shelf by default */) == 1;
+ if (mMenuRow == null || removeShelf) {
return null;
}
-
if (mMenuRow.getMenuView() == null) {
mMenuRow.createMenu(this, mEntry.getSbn());
mMenuRow.setAppName(mAppName);
@@ -1555,7 +1566,8 @@
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
OnUserInteractionCallback onUserInteractionCallback,
- Optional<BubblesManager> bubblesManagerOptional) {
+ Optional<BubblesManager> bubblesManagerOptional,
+ NotificationGutsManager gutsManager) {
mEntry = entry;
mAppName = appName;
if (mMenuRow == null) {
@@ -1584,6 +1596,7 @@
}
mOnUserInteractionCallback = onUserInteractionCallback;
mBubblesManagerOptional = bubblesManagerOptional;
+ mNotificationGutsManager = gutsManager;
cacheIsSystemNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 05b1dba..cb2af54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -154,7 +154,8 @@
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
- mBubblesManagerOptional
+ mBubblesManagerOptional,
+ mNotificationGutsManager
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 71e1d12..79c3007 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -18,12 +18,14 @@
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -31,6 +33,7 @@
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -44,6 +47,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.TransformableView;
@@ -458,7 +462,7 @@
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
if (mContainingNotification != null) {
- applyBubbleAction(mExpandedChild, mContainingNotification.getEntry());
+ applySystemActions(mExpandedChild, mContainingNotification.getEntry());
}
}
@@ -500,7 +504,7 @@
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
if (mContainingNotification != null) {
- applyBubbleAction(mHeadsUpChild, mContainingNotification.getEntry());
+ applySystemActions(mHeadsUpChild, mContainingNotification.getEntry());
}
}
@@ -1161,8 +1165,8 @@
mForceSelectNextLayout = true;
mPreviousExpandedRemoteInputIntent = null;
mPreviousHeadsUpRemoteInputIntent = null;
- applyBubbleAction(mExpandedChild, entry);
- applyBubbleAction(mHeadsUpChild, entry);
+ applySystemActions(mExpandedChild, entry);
+ applySystemActions(mHeadsUpChild, entry);
}
private void updateAllSingleLineViews() {
@@ -1340,6 +1344,14 @@
NOTIFICATION_BUBBLES, 0) == 1;
}
+ /**
+ * Setup icon buttons provided by System UI.
+ */
+ private void applySystemActions(View layout, NotificationEntry entry) {
+ applySnoozeAction(layout);
+ applyBubbleAction(layout, entry);
+ }
+
private void applyBubbleAction(View layout, NotificationEntry entry) {
if (layout == null || mContainingNotification == null || mPeopleIdentifier == null) {
return;
@@ -1359,8 +1371,8 @@
&& entry.getBubbleMetadata() != null;
if (showButton) {
Drawable d = mContext.getResources().getDrawable(entry.isBubble()
- ? R.drawable.ic_stop_bubble
- : R.drawable.ic_create_bubble);
+ ? R.drawable.bubble_ic_stop_bubble
+ : R.drawable.bubble_ic_create_bubble);
mContainingNotification.updateNotificationColor();
final int tint = mContainingNotification.getNotificationColor();
d.setTint(tint);
@@ -1387,6 +1399,45 @@
}
}
+ private void applySnoozeAction(View layout) {
+ if (layout == null || mContainingNotification == null) {
+ return;
+ }
+ ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
+ View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
+ LinearLayout actionContainerLayout =
+ layout.findViewById(com.android.internal.R.id.actions_container_layout);
+ if (snoozeButton == null || actionContainer == null || actionContainerLayout == null) {
+ return;
+ }
+ final boolean showSnooze = Settings.Secure.getInt(mContext.getContentResolver(),
+ SHOW_NOTIFICATION_SNOOZE, 0) == 1;
+ if (!showSnooze) {
+ snoozeButton.setVisibility(GONE);
+ return;
+ }
+
+ Resources res = mContext.getResources();
+ Drawable snoozeDrawable = res.getDrawable(R.drawable.ic_snooze);
+ mContainingNotification.updateNotificationColor();
+ snoozeDrawable.setTint(mContainingNotification.getNotificationColor());
+ snoozeButton.setImageDrawable(snoozeDrawable);
+
+ final NotificationSnooze snoozeGuts = (NotificationSnooze) LayoutInflater.from(mContext)
+ .inflate(R.layout.notification_snooze, null, false);
+ final String snoozeDescription = res.getString(
+ R.string.notification_menu_snooze_description);
+ final NotificationMenuRowPlugin.MenuItem snoozeMenuItem =
+ new NotificationMenuRow.NotificationMenuItem(
+ mContext, snoozeDescription, snoozeGuts, R.drawable.ic_snooze);
+ snoozeButton.setContentDescription(
+ mContext.getResources().getString(R.string.notification_menu_snooze_description));
+ snoozeButton.setOnClickListener(
+ mContainingNotification.getSnoozeClickListener(snoozeMenuItem));
+ snoozeButton.setVisibility(VISIBLE);
+ actionContainer.setVisibility(VISIBLE);
+ }
+
private void applySmartReplyView(
SmartRepliesAndActions smartRepliesAndActions,
NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index bd22893..5c225e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -257,7 +257,7 @@
// TODO(b/12836565) - prototyping only adjustment
if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
// This will keep the clock at the top for AOD
- darkAmount = 0f;
+ return (int) (clockY + burnInPreventionOffsetY() + mEmptyDragAmount);
}
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 5e883be..289ff71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -39,7 +39,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
-import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -77,7 +76,6 @@
private final KeyguardStateController mKeyguardStateController;
private final Resources mResources;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
- private final AuthController mAuthController;
private boolean mKeyguardShowing;
private boolean mKeyguardJustShown;
private boolean mBlockUpdates;
@@ -326,8 +324,7 @@
@Nullable DockManager dockManager,
KeyguardStateController keyguardStateController,
@Main Resources resources,
- HeadsUpManagerPhone headsUpManagerPhone,
- AuthController authController) {
+ HeadsUpManagerPhone headsUpManagerPhone) {
mLockscreenGestureLogger = lockscreenGestureLogger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -342,7 +339,6 @@
mKeyguardStateController = keyguardStateController;
mResources = resources;
mHeadsUpManagerPhone = headsUpManagerPhone;
- mAuthController = authController;
mKeyguardIndicationController.setLockIconController(this);
}
@@ -508,7 +504,7 @@
* @return true if the visibility changed
*/
private boolean updateIconVisibility() {
- if (mAuthController.isUdfpsEnrolled()) {
+ if (mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
boolean changed = mLockIcon.getVisibility() == GONE;
mLockIcon.setVisibility(GONE);
return changed;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index f2ae3da7..ac91b70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -19,7 +19,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -32,10 +31,14 @@
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.ArrayList;
import java.util.List;
@@ -175,6 +178,16 @@
updateIconLayoutParams(mContext);
}
+ /**
+ * Update position of the view, with optional animation
+ */
+ public void updatePosition(int x, AnimationProperties props, boolean animate) {
+ if (mAodIcons != null) {
+ PropertyAnimator.setProperty(mAodIcons, AnimatableProperty.TRANSLATION_X, x, props,
+ animate);
+ }
+ }
+
public void setupShelf(NotificationShelfController notificationShelfController) {
mShelfIcons = notificationShelfController.getShelfIcons();
notificationShelfController.setCollapsedIcons(mNotificationIcons);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e9a7132..231d157 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -873,7 +873,7 @@
clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
- mAuthController.isUdfpsEnrolled());
+ mUpdateMonitor.isUdfpsEnrolled());
mClockPositionAlgorithm.run(mClockPositionResult);
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock);
@@ -914,7 +914,7 @@
- Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
- mKeyguardStatusViewController.getLogoutButtonHeight();
- if (mAuthController.isUdfpsEnrolled()) {
+ if (mUpdateMonitor.isUdfpsEnrolled()) {
availableSpace = mNotificationStackScrollLayoutController.getHeight()
- minPadding - shelfSize
- (mStatusBar.getDisplayHeight() - mAuthController.getUdfpsRegion().top);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index af2f3e5..a930a89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -22,13 +22,13 @@
import android.view.WindowManager;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.wm.shell.bubbles.Bubbles;
import java.util.ArrayList;
import java.util.Optional;
@@ -51,7 +51,7 @@
private final int mDisplayId;
protected final Lazy<StatusBar> mStatusBarLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
- private final Optional<Lazy<Bubbles>> mBubblesOptional;
+ private final Optional<Bubbles> mBubblesOptional;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -64,7 +64,7 @@
WindowManager windowManager,
Lazy<StatusBar> statusBarLazy,
Lazy<AssistManager> assistManagerLazy,
- Optional<Lazy<Bubbles>> bubblesOptional
+ Optional<Bubbles> bubblesOptional
) {
mCommandQueue = commandQueue;
mStatusBarStateController = statusBarStateController;
@@ -135,7 +135,7 @@
getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
} else if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().get().collapseStack();
+ mBubblesOptional.get().collapseStack();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a8d4104..5c7f54b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -144,7 +144,6 @@
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -229,6 +228,7 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index b69da85..13d5bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -31,7 +31,6 @@
import com.android.systemui.InitController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -98,6 +97,7 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index e79d432..4b4e1df 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -26,7 +26,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
-import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.lang.reflect.InvocationTargetException;
@@ -104,11 +103,6 @@
* Creates the QuickQSPanel.
*/
QuickQSPanel createQuickQSPanel();
-
- /**
- * Creates the QSCustomizer.
- */
- QSCustomizer createQSCustomizer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index ad596c2..844f12e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -27,10 +27,10 @@
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.INotificationManager;
import android.app.Notification;
@@ -53,8 +53,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.bubbles.BubbleEntry;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
@@ -80,6 +78,8 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.bubbles.BubbleEntry;
+import com.android.wm.shell.bubbles.Bubbles;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 91ae08e..bdca503 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -18,21 +18,27 @@
import android.app.IActivityManager;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.view.IWindowManager;
+import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.bubbles.Bubbles;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.WMSingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.wm.shell.ShellDump;
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.AnimationThread;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -116,6 +122,18 @@
@WMSingleton
@Provides
+ static FloatingContentCoordinator provideFloatingContentCoordinator() {
+ return new FloatingContentCoordinator();
+ }
+
+ @WMSingleton
+ @Provides
+ static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
+ return new WindowManagerShellWrapper();
+ }
+
+ @WMSingleton
+ @Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
IActivityManager activityManager,
PipTouchHandler pipTouchHandler) {
@@ -166,8 +184,21 @@
@BindsOptionalOf
abstract SplitScreen optionalSplitScreen();
- @BindsOptionalOf
- abstract Bubbles optionalBubbles();
+ @WMSingleton
+ @Provides
+ static Optional<Bubbles> provideBubbles(Context context,
+ FloatingContentCoordinator floatingContentCoordinator,
+ IStatusBarService statusBarService,
+ WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
+ LauncherApps launcherApps,
+ UiEventLogger uiEventLogger,
+ @Main Handler mainHandler,
+ ShellTaskOrganizer organizer) {
+ return Optional.of(BubbleController.create(context, null /* synchronizer */,
+ floatingContentCoordinator, statusBarService, windowManager,
+ windowManagerShellWrapper, launcherApps, uiEventLogger, mainHandler, organizer));
+ }
@WMSingleton
@Provides
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e5847b0..f1c687f 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -63,7 +63,7 @@
</intent-filter>
</receiver>
- <activity android:name="com.android.systemui.bubbles.BubblesTestActivity"
+ <activity android:name=".wmshell.BubblesTestActivity"
android:allowEmbedded="true"
android:documentLaunchMode="always"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index 594f0b1..cbd6e86 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -116,6 +116,13 @@
filter.add(s -> s.startsWith("com.android.systemui")
|| s.startsWith("com.android.keyguard"));
+ // Screenshots run in an isolated process and should not be run
+ // with the main process dependency graph because it will not exist
+ // at runtime and could lead to incorrect tests which assume
+ // the main SystemUI process. Therefore, exclude this package
+ // from the base class whitelist.
+ filter.add(s -> !s.startsWith("com.android.systemui.screenshot"));
+
try {
return scanner.getClassPathEntries(filter);
} catch (IOException e) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index caab2ab..d78090a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -201,7 +201,7 @@
when(mTelephonyManager.getServiceStateForSubscriber(anyInt()))
.thenReturn(new ServiceState());
when(mLockPatternUtils.getLockSettings()).thenReturn(mLockSettings);
- when(mAuthController.isUdfpsEnrolled()).thenReturn(false);
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
mSpiedContext.addMockSystemService(TrustManager.class, mTrustManager);
mSpiedContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
mSpiedContext.addMockSystemService(BiometricManager.class, mBiometricManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 3494bd6..fa29fd4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -49,7 +49,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.recents.OverviewProxyService;
import org.junit.Before;
@@ -66,7 +65,6 @@
private KeyButtonView mKeyButtonView;
private MetricsLogger mMetricsLogger;
- private Bubbles mBubbles;
private UiEventLogger mUiEventLogger;
private InputManager mInputManager = mock(InputManager.class);
@Captor
@@ -76,7 +74,6 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
- mBubbles = mDependency.injectMockDependency(Bubbles.class);
mDependency.injectMockDependency(OverviewProxyService.class);
mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index 8039192..c050b62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -52,7 +52,7 @@
private MetricsLogger mMetricsLogger;
private QSDetail mQsDetail;
- private QSPanel mQsPanel;
+ private QSPanelController mQsPanelController;
private QuickStatusBarHeader mQuickHeader;
private ActivityStarter mActivityStarter;
private DetailAdapter mMockDetailAdapter;
@@ -68,9 +68,9 @@
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
- mQsPanel = mock(QSPanel.class);
+ mQsPanelController = mock(QSPanelController.class);
mQuickHeader = mock(QuickStatusBarHeader.class);
- mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(QSFooter.class));
+ mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class));
mMockDetailAdapter = mock(DetailAdapter.class);
when(mMockDetailAdapter.createDetailView(any(), any(), any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index bf0e084..64ef6dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import org.junit.Before;
@@ -61,6 +62,12 @@
@Mock
private QSTileHost mQSTileHost;
@Mock
+ private QSCustomizerController mQSCustomizerController;
+ @Mock
+ private QSTileRevealController.Factory mQSTileRevealControllerFactory;
+ @Mock
+ private QSTileRevealController mQSTileRevealController;
+ @Mock
private MediaHost mMediaHost;
@Mock
private MetricsLogger mMetricsLogger;
@@ -70,15 +77,19 @@
QSTileImpl mQSTile;
@Mock
QSTileView mQSTileView;
+ @Mock
+ PagedTileLayout mPagedTileLayout;
private QSPanelControllerBase<QSPanel> mController;
/** Implementation needed to ensure we have a reflectively-available class name. */
private static class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
- MetricsLogger metricsLogger,
- UiEventLogger uiEventLogger, DumpManager dumpManager) {
- super(view, host, metricsLogger, uiEventLogger, dumpManager);
+ QSCustomizerController qsCustomizerController,
+ QSTileRevealController.Factory qsTileRevealControllerFactory,
+ MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) {
+ super(view, host, qsCustomizerController, qsTileRevealControllerFactory, metricsLogger,
+ uiEventLogger, dumpManager);
}
}
@@ -91,11 +102,14 @@
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED);
when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED);
+ when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout);
when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
+ when(mQSTileRevealControllerFactory.create(any())).thenReturn(mQSTileRevealController);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
- mMetricsLogger, mUiEventLogger, mDumpManager);
+ mQSCustomizerController, mQSTileRevealControllerFactory, mMetricsLogger,
+ mUiEventLogger, mDumpManager);
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 0ba0214..bce376a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
@@ -58,6 +59,12 @@
@Mock
private QSTileHost mQSTileHost;
@Mock
+ private QSCustomizerController mQSCustomizerController;
+ @Mock
+ private QSTileRevealController.Factory mQSTileRevealControllerFactory;
+ @Mock
+ private QSTileRevealController mQSTileRevealController;
+ @Mock
private MediaHost mMediaHost;
@Mock
private MetricsLogger mMetricsLogger;
@@ -75,6 +82,9 @@
QSTileImpl mQSTile;
@Mock
QSTileView mQSTileView;
+ @Mock
+ PagedTileLayout mPagedTileLayout;
+
private QSPanelController mController;
@@ -85,14 +95,16 @@
when(mQSPanel.getMediaHost()).thenReturn(mMediaHost);
when(mQSPanel.isAttachedToWindow()).thenReturn(true);
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
+ when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout);
when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
when(mBrightnessControllerFactory.create(any(ToggleSlider.class)))
.thenReturn(mBrightnessController);
+ when(mQSTileRevealControllerFactory.create(any())).thenReturn(mQSTileRevealController);
mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService,
- mQSTileHost, mDumpManager, mMetricsLogger, mUiEventLogger,
- mBrightnessControllerFactory);
+ mQSTileHost, mQSCustomizerController, mQSTileRevealControllerFactory, mDumpManager,
+ mMetricsLogger, mUiEventLogger, mBrightnessControllerFactory);
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index e38d54b..450ffac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -39,7 +39,6 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -65,12 +64,8 @@
@Mock
private QSTileHost mHost;
@Mock
- private QSCustomizer mCustomizer;
- @Mock
private QSTileImpl dndTile;
@Mock
- private QSTileImpl mNonTile;
- @Mock
private QSPanelControllerBase.TileRecord mDndTileRecord;
@Mock
private QSLogger mQSLogger;
@@ -84,7 +79,6 @@
@Mock
private ActivityStarter mActivityStarter;
private UiEventLoggerFake mUiEventLogger;
- private String mCachedSpecs = "";
@Before
public void setup() throws Exception {
@@ -113,8 +107,6 @@
when(dndTile.getTileSpec()).thenReturn("dnd");
when(mHost.getTiles()).thenReturn(Collections.emptyList());
when(mHost.createTileView(any(), anyBoolean())).thenReturn(mQSTileView);
-
- mQsPanel.setCustomizer(mCustomizer);
mQsPanel.addTile(mDndTileRecord);
mQsPanel.setCallback(mCallback);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index 204de929..3d53062 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -15,7 +15,6 @@
package com.android.systemui.qs.customize;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
@@ -31,6 +30,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
@@ -40,17 +41,20 @@
public class TileAdapterTest extends SysuiTestCase {
private TileAdapter mTileAdapter;
+ @Mock
+ private QSTileHost mQSTileHost;
@Before
public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
TestableLooper.get(this).runWithLooper(() -> mTileAdapter =
- new TileAdapter(mContext, new UiEventLoggerFake()));
+ new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake()));
}
@Test
public void testResetNotifiesHost() {
- QSTileHost host = mock(QSTileHost.class);
- mTileAdapter.resetTileSpecs(host, Collections.emptyList());
- verify(host).changeTiles(any(), any());
+ mTileAdapter.resetTileSpecs(Collections.emptyList());
+ verify(mQSTileHost).changeTiles(any(), any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
new file mode 100644
index 0000000..a75c39c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
@@ -0,0 +1,142 @@
+/*
+ * 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.screenshot;
+
+import android.content.pm.ActivityInfo;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.HardwareRenderer;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.os.RemoteException;
+import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureConnection;
+import android.view.Surface;
+
+/**
+ * An IScrollCaptureConnection which returns a sequence of solid filled rectangles in the
+ * locations requested, in alternating colors.
+ */
+class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
+ private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE};
+ private IScrollCaptureCallbacks mCallbacks;
+ private Surface mSurface;
+ private Paint mPaint;
+ private int mNextColor;
+ private HwuiContext mHwuiContext;
+
+ FakeScrollCaptureConnection(IScrollCaptureCallbacks cb) {
+ mCallbacks = cb;
+ }
+
+ @Override
+ public void startCapture(Surface surface) {
+ mSurface = surface;
+ mHwuiContext = new HwuiContext(false, surface);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setStyle(Paint.Style.FILL);
+ try {
+ mCallbacks.onCaptureStarted();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void requestImage(Rect rect) {
+ Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height());
+ mPaint.setColor(mColors[mNextColor]);
+ canvas.drawRect(rect, mPaint);
+ mNextColor = (mNextColor++) % mColors.length;
+ long frameNumber = mSurface.getNextFrameNumber();
+ mHwuiContext.unlockAndPost(canvas);
+ try {
+ mCallbacks.onCaptureBufferSent(frameNumber, rect);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void endCapture() {
+ try {
+ mCallbacks.onConnectionClosed();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ } finally {
+ mHwuiContext.destroy();
+ mSurface = null;
+ mCallbacks = null;
+ }
+ }
+
+ // From android.view.Surface, but issues render requests synchronously with waitForPresent(true)
+ private static final class HwuiContext {
+ private final RenderNode mRenderNode;
+ private final HardwareRenderer mHardwareRenderer;
+ private RecordingCanvas mCanvas;
+ private final boolean mIsWideColorGamut;
+
+ HwuiContext(boolean isWideColorGamut, Surface surface) {
+ mRenderNode = RenderNode.create("HwuiCanvas", null);
+ mRenderNode.setClipToBounds(false);
+ mRenderNode.setForceDarkAllowed(false);
+ mIsWideColorGamut = isWideColorGamut;
+
+ mHardwareRenderer = new HardwareRenderer();
+ mHardwareRenderer.setContentRoot(mRenderNode);
+ mHardwareRenderer.setSurface(surface, true);
+ mHardwareRenderer.setColorMode(
+ isWideColorGamut
+ ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
+ : ActivityInfo.COLOR_MODE_DEFAULT);
+ mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
+ mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+
+ Canvas lockCanvas(int width, int height) {
+ if (mCanvas != null) {
+ throw new IllegalStateException("Surface was already locked!");
+ }
+ mCanvas = mRenderNode.beginRecording(width, height);
+ return mCanvas;
+ }
+
+ void unlockAndPost(Canvas canvas) {
+ if (canvas != mCanvas) {
+ throw new IllegalArgumentException("canvas object must be the same instance that "
+ + "was previously returned by lockCanvas");
+ }
+ mRenderNode.endRecording();
+ mCanvas = null;
+ mHardwareRenderer.createRenderRequest()
+ .setVsyncTime(System.nanoTime())
+ .setWaitForPresent(true) // sync!
+ .syncAndDraw();
+ }
+
+ void destroy() {
+ mHardwareRenderer.destroy();
+ }
+
+ boolean isWideColorGamut() {
+ return mIsWideColorGamut;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
new file mode 100644
index 0000000..4aa730e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.screenshot;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+import android.view.IScrollCaptureCallbacks;
+import android.view.IWindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
+import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ScrollCaptureClientTest extends SysuiTestCase {
+ private Context mContext;
+ private IWindowManager mWm;
+
+ @Spy private TestableConsumer<Session> mSessionConsumer;
+ @Spy private TestableConsumer<Connection> mConnectionConsumer;
+ @Spy private TestableConsumer<CaptureResult> mResultConsumer;
+ @Mock private Runnable mRunnable;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ DisplayManager displayManager = requireNonNull(
+ context.getSystemService(DisplayManager.class));
+ mContext = context.createDisplayContext(
+ displayManager.getDisplay(Display.DEFAULT_DISPLAY));
+ mWm = mock(IWindowManager.class);
+ }
+
+ @Test
+ public void testBasicClientFlow() throws RemoteException {
+ doAnswer((Answer<Void>) invocation -> {
+ IScrollCaptureCallbacks cb = invocation.getArgument(3);
+ cb.onConnected(
+ new FakeScrollCaptureConnection(cb),
+ /* scrollBounds */ new Rect(0, 0, 100, 100),
+ /* positionInWindow */ new Point(0, 0));
+ return null;
+ }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(),
+ /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class));
+
+ // Create client
+ ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
+
+ client.request(Display.DEFAULT_DISPLAY, mConnectionConsumer);
+ verify(mConnectionConsumer, timeout(100)).accept(any(Connection.class));
+
+ Connection conn = mConnectionConsumer.getValue();
+
+ conn.start(5, mSessionConsumer);
+ verify(mSessionConsumer, timeout(100)).accept(any(Session.class));
+
+ Session session = mSessionConsumer.getValue();
+ Rect request = new Rect(0, 0, session.getMaxTileWidth(), session.getMaxTileHeight());
+
+ session.requestTile(request, mResultConsumer);
+ verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class));
+
+ CaptureResult result = mResultConsumer.getValue();
+ assertThat(result.requested).isEqualTo(request);
+ assertThat(result.captured).isEqualTo(result.requested);
+ assertThat(result.image).isNotNull();
+
+ session.end(mRunnable);
+ verify(mRunnable, timeout(100)).run();
+
+ // TODO verify image
+ // TODO test threading
+ // TODO test failures
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
copy to packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
index 24768cd..a554e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
@@ -13,17 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bubbles.storage
-import android.annotation.DimenRes
-import android.annotation.UserIdInt
+package com.android.systemui.screenshot;
-data class BubbleEntity(
- @UserIdInt val userId: Int,
- val packageName: String,
- val shortcutId: String,
- val key: String,
- val desiredHeight: Int,
- @DimenRes val desiredHeightResId: Int,
- val title: String? = null
-)
+import java.util.function.Consumer;
+
+/** Accepts and retains the most recent value for verification */
+class TestableConsumer<T> implements Consumer<T> {
+ T mValue;
+
+ @Override
+ public void accept(T t) {
+ mValue = t;
+ }
+
+ public T getValue() {
+ return mValue;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 10eca00..7fb7b86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -36,7 +36,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.wm.shell.bubbles.Bubbles;
import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index cd46dda..05cf33a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -42,7 +42,6 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.wm.shell.bubbles.Bubbles;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 4698b8e..f7dfe0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -76,12 +76,12 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.systemui.wmshell.BubblesTestActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 48375e0..baae8fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -45,8 +45,6 @@
import com.android.systemui.R;
import com.android.systemui.TestableDependency;
-import com.android.systemui.bubbles.Bubbles;
-import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.FalsingManager;
@@ -71,6 +69,8 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.InflatedSmartReplies;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.systemui.wmshell.BubblesTestActivity;
+import com.android.wm.shell.bubbles.Bubbles;
import org.mockito.ArgumentCaptor;
@@ -432,7 +432,8 @@
mStatusBarStateController,
mPeopleNotificationIdentifier,
mock(OnUserInteractionCallback.class),
- Optional.of(mock(BubblesManager.class)));
+ Optional.of(mock(BubblesManager.class)),
+ mock(NotificationGutsManager.class));
row.setAboveShelfChangedListener(aboveShelf -> { });
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 72a0258..aca3424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -32,7 +32,6 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -81,25 +80,21 @@
private Resources mResources;
@Mock
private HeadsUpManagerPhone mHeadsUpManagerPhone;
- @Mock
- private AuthController mAuthController;
private LockscreenLockIconController mLockIconController;
private OnAttachStateChangeListener mOnAttachStateChangeListener;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mAuthController.isUdfpsEnrolled()).thenReturn(false);
when(mLockIcon.getContext()).thenReturn(mContext);
mLockIconController = new LockscreenLockIconController(
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
- mHeadsUpManagerPhone, mAuthController);
+ mHeadsUpManagerPhone);
ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(OnAttachStateChangeListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index b0086ef..7c8b413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -37,7 +37,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -48,6 +47,7 @@
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.wm.shell.bubbles.Bubbles;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index f81672a..3e9fd51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -30,12 +30,12 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.wm.shell.bubbles.Bubbles;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index a1d419c..858227f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -27,7 +27,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -35,6 +34,7 @@
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.wm.shell.bubbles.Bubbles;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 3b123f6..e1f8596 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
@@ -204,7 +205,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mAuthController.isUdfpsEnrolled()).thenReturn(false);
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
when(mResources.getConfiguration()).thenReturn(mConfiguration);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 9f096da..2f9b601 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -83,7 +83,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
@@ -146,6 +145,7 @@
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -337,7 +337,7 @@
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> mStatusBar, () -> mAssistManager, Optional.of(() -> mBubbles));
+ () -> mStatusBar, () -> mAssistManager, Optional.of(mBubbles));
mStatusBar = new StatusBar(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 88d0401..a46e563 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -64,14 +64,6 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.Bubble;
-import com.android.systemui.bubbles.BubbleData;
-import com.android.systemui.bubbles.BubbleDataRepository;
-import com.android.systemui.bubbles.BubbleEntry;
-import com.android.systemui.bubbles.BubbleLogger;
-import com.android.systemui.bubbles.BubblePositioner;
-import com.android.systemui.bubbles.BubbleStackView;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -102,6 +94,14 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.bubbles.BubbleData;
+import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleEntry;
+import com.android.wm.shell.bubbles.BubbleLogger;
+import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.google.common.collect.ImmutableList;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
rename to packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
index 43d2ad1..f4d96a12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.bubbles;
+package com.android.systemui.wmshell;
import android.app.Activity;
import android.content.Intent;
@@ -27,8 +27,7 @@
*/
public class BubblesTestActivity extends Activity {
- public static final String BUBBLE_ACTIVITY_OPENED =
- "com.android.systemui.bubbles.BUBBLE_ACTIVITY_OPENED";
+ public static final String BUBBLE_ACTIVITY_OPENED = "BUBBLE_ACTIVITY_OPENED";
@Override
public void onCreate(Bundle savedInstanceState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 99c8ca4..d8033db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -60,13 +60,6 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleData;
-import com.android.systemui.bubbles.BubbleDataRepository;
-import com.android.systemui.bubbles.BubbleEntry;
-import com.android.systemui.bubbles.BubbleLogger;
-import com.android.systemui.bubbles.BubblePositioner;
-import com.android.systemui.bubbles.BubbleStackView;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -95,6 +88,13 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.bubbles.BubbleData;
+import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleEntry;
+import com.android.wm.shell.bubbles.BubbleLogger;
+import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.FloatingContentCoordinator;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 2273bc4..fd39b6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -22,13 +22,13 @@
import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleData;
-import com.android.systemui.bubbles.BubbleDataRepository;
-import com.android.systemui.bubbles.BubbleLogger;
-import com.android.systemui.bubbles.BubblePositioner;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleData;
+import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleLogger;
+import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.FloatingContentCoordinator;
/**
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index ad85784..a1ad72c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -84,7 +84,6 @@
import com.android.internal.util.SyncResultReceiver;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.SystemService.TargetUser;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -170,7 +169,7 @@
// beneath it is brought back to top. Ideally, we should just hide the UI and
// bring it back when the activity resumes.
synchronized (mLock) {
- visitServicesLocked((s) -> s.destroyFinishedSessionsLocked());
+ visitServicesLocked((s) -> s.forceRemoveFinishedSessionsLocked());
}
mUi.hideAll(null);
}
@@ -386,18 +385,18 @@
}
// Called by Shell command.
- void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
- Slog.i(TAG, "destroySessions() for userId " + userId);
+ void removeAllSessions(@UserIdInt int userId, IResultReceiver receiver) {
+ Slog.i(TAG, "removeAllSessions() for userId " + userId);
enforceCallingPermissionForManagement();
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- service.destroySessionsLocked();
+ service.forceRemoveAllSessionsLocked();
}
} else {
- visitServicesLocked((s) -> s.destroySessionsLocked());
+ visitServicesLocked((s) -> s.forceRemoveAllSessionsLocked());
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 864ead1..3212698 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -215,14 +215,14 @@
@GuardedBy("mLock")
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
- destroySessionsLocked();
+ forceRemoveAllSessionsLocked();
final boolean enabledChanged = super.updateLocked(disabled);
if (enabledChanged) {
if (!isEnabledLocked()) {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
- session.removeSelfLocked();
+ session.removeFromServiceLocked();
}
}
sendStateToClients(/* resetClient= */ false);
@@ -442,7 +442,7 @@
if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
if (finished) {
- session.removeSelfLocked();
+ session.removeFromServiceLocked();
}
}
@@ -457,7 +457,7 @@
Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
return;
}
- session.removeSelfLocked();
+ session.removeFromServiceLocked();
}
@GuardedBy("mLock")
@@ -483,7 +483,7 @@
componentName.getPackageName());
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
- destroySessionsLocked();
+ forceRemoveAllSessionsLocked();
} else {
Slog.w(TAG, "disableOwnedServices(): ignored because current service ("
+ serviceInfo + ") does not match Settings (" + autoFillService + ")");
@@ -1107,35 +1107,41 @@
}
@GuardedBy("mLock")
- void destroySessionsLocked() {
- if (mSessions.size() == 0) {
+ void forceRemoveAllSessionsLocked() {
+ final int sessionCount = mSessions.size();
+ if (sessionCount == 0) {
mUi.destroyAll(null, null, false);
return;
}
- while (mSessions.size() > 0) {
- mSessions.valueAt(0).forceRemoveSelfLocked();
+
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ mSessions.valueAt(i).forceRemoveFromServiceLocked();
}
}
@GuardedBy("mLock")
- void destroySessionsForAugmentedAutofillOnlyLocked() {
+ void forceRemoveForAugmentedOnlySessionsLocked() {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
- mSessions.valueAt(i).forceRemoveSelfIfForAugmentedAutofillOnlyLocked();
+ mSessions.valueAt(i).forceRemoveFromServiceIfForAugmentedOnlyLocked();
}
}
+ /**
+ * This method is called exclusively in response to {@code Intent.ACTION_CLOSE_SYSTEM_DIALOGS}.
+ * The method removes all sessions that are finished but showing SaveUI due to how SaveUI is
+ * managed (see b/64940307). Otherwise it will remove any augmented autofill generated windows.
+ */
// TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
@GuardedBy("mLock")
- void destroyFinishedSessionsLocked() {
+ void forceRemoveFinishedSessionsLocked() {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
if (session.isSavingLocked()) {
if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
- session.forceRemoveSelfLocked();
- }
- else {
+ session.forceRemoveFromServiceLocked();
+ } else {
session.destroyAugmentedAutofillWindowsLocked();
}
}
@@ -1261,7 +1267,7 @@
Slog.v(TAG, "updateRemoteAugmentedAutofillService(): "
+ "destroying old remote service");
}
- destroySessionsForAugmentedAutofillOnlyLocked();
+ forceRemoveForAugmentedOnlySessionsLocked();
mRemoteAugmentedAutofillService.unbind();
mRemoteAugmentedAutofillService = null;
mRemoteAugmentedAutofillServiceInfo = null;
@@ -1663,7 +1669,7 @@
Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
+ sessionToRemove.getActivityTokenLocked() + ")");
}
- sessionToRemove.removeSelfLocked();
+ sessionToRemove.removeFromServiceLocked();
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index bbe37a5..68e6290 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -355,7 +355,7 @@
latch.countDown();
}
};
- return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
+ return requestSessionCommon(pw, latch, () -> mService.removeAllSessions(userId, receiver));
}
private int requestList(PrintWriter pw) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0302b22..b48d71a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -227,8 +227,8 @@
private boolean mHasCallback;
/**
- * Extras sent by service on {@code onFillRequest()} calls; the first non-null extra is saved
- * and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
+ * Extras sent by service on {@code onFillRequest()} calls; the most recent non-null extra is
+ * saved and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
*/
@GuardedBy("mLock")
private Bundle mClientState;
@@ -1086,7 +1086,7 @@
if (showMessage) {
getUiForShowing().showError(message, this);
}
- removeSelf();
+ removeFromService();
}
// FillServiceCallbacks
@@ -1111,7 +1111,7 @@
}
// Nothing left to do...
- removeSelf();
+ removeFromService();
}
// FillServiceCallbacks
@@ -1147,7 +1147,7 @@
if (showMessage) {
getUiForShowing().showError(message, this);
}
- removeSelf();
+ removeFromService();
}
/**
@@ -1179,7 +1179,7 @@
@Override
public void onServiceDied(@NonNull RemoteFillService service) {
Slog.w(TAG, "removing session because service died");
- forceRemoveSelfLocked();
+ forceRemoveFromServiceLocked();
}
// AutoFillUiCallback
@@ -1199,7 +1199,7 @@
}
fillInIntent = createAuthFillInIntentLocked(requestId, extras);
if (fillInIntent == null) {
- forceRemoveSelfLocked();
+ forceRemoveFromServiceLocked();
return;
}
}
@@ -1255,7 +1255,7 @@
}
}
mHandler.sendMessage(obtainMessage(
- Session::removeSelf, this));
+ Session::removeFromService, this));
}
// AutoFillUiCallback
@@ -1327,7 +1327,7 @@
@Override
public void cancelSession() {
synchronized (mLock) {
- removeSelfLocked();
+ removeFromServiceLocked();
}
}
@@ -1347,7 +1347,7 @@
return;
}
if (intent == null) {
- removeSelfLocked();
+ removeFromServiceLocked();
}
}
mHandler.sendMessage(obtainMessage(
@@ -1401,13 +1401,13 @@
// Typically happens when app explicitly called cancel() while the service was showing
// the auth UI.
Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
- removeSelf();
+ removeFromService();
return;
}
final FillResponse authenticatedResponse = mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
Slog.w(TAG, "no authenticated response");
- removeSelf();
+ removeFromService();
return;
}
@@ -1418,7 +1418,7 @@
final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx);
if (dataset == null) {
Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
- removeSelf();
+ removeFromService();
return;
}
}
@@ -1504,7 +1504,7 @@
Slog.d(TAG, "Rejecting empty/invalid auth result");
}
mService.resetLastAugmentedAutofillResponse();
- removeSelfLocked();
+ removeFromServiceLocked();
return;
}
@@ -2715,7 +2715,7 @@
return;
}
if (sDebug) Slog.d(TAG, "Finishing session because URL bar changed");
- forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
+ forceRemoveFromServiceLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
return;
}
if (!Objects.equals(value, viewState.getCurrentValue())) {
@@ -3226,7 +3226,7 @@
}
// Nothing to be done, but need to notify client.
notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds);
- removeSelf();
+ removeFromService();
} else {
if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
if (sVerbose) {
@@ -3393,20 +3393,6 @@
}
@GuardedBy("mLock")
- private void logAugmentedAutofillRequestLocked(int mode,
- ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted,
- Boolean isInline) {
- final String historyItem =
- "aug:id=" + id + " u=" + uid + " m=" + mode
- + " a=" + ComponentName.flattenToShortString(mComponentName)
- + " f=" + focusedId
- + " s=" + augmentedRemoteServiceName
- + " w=" + isWhitelisted
- + " i=" + isInline;
- mService.getMaster().logRequestLocked(historyItem);
- }
-
- @GuardedBy("mLock")
private void cancelAugmentedAutofillLocked() {
final RemoteAugmentedAutofillService remoteService = mService
.getRemoteAugmentedAutofillServiceLocked();
@@ -3574,7 +3560,7 @@
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
if (fillInIntent == null) {
- forceRemoveSelfLocked();
+ forceRemoveFromServiceLocked();
return;
}
final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
@@ -3923,12 +3909,12 @@
}
/**
- * Cleans up this session.
+ * Destroy this session and perform any clean up work.
*
* <p>Typically called in 2 scenarios:
*
* <ul>
- * <li>When the session naturally finishes (i.e., from {@link #removeSelfLocked()}.
+ * <li>When the session naturally finishes (i.e., from {@link #removeFromServiceLocked()}.
* <li>When the service hosting the session is finished (for example, because the user
* disabled it).
* </ul>
@@ -3990,32 +3976,32 @@
}
/**
- * Cleans up this session and remove it from the service always, even if it does have a pending
+ * Destroy this session and remove it from the service always, even if it does have a pending
* Save UI.
*/
@GuardedBy("mLock")
- void forceRemoveSelfLocked() {
- forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN);
+ void forceRemoveFromServiceLocked() {
+ forceRemoveFromServiceLocked(AutofillManager.STATE_UNKNOWN);
}
@GuardedBy("mLock")
- void forceRemoveSelfIfForAugmentedAutofillOnlyLocked() {
+ void forceRemoveFromServiceIfForAugmentedOnlyLocked() {
if (sVerbose) {
- Slog.v(TAG, "forceRemoveSelfIfForAugmentedAutofillOnly(" + this.id + "): "
+ Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): "
+ mForAugmentedAutofillOnly);
}
if (!mForAugmentedAutofillOnly) return;
- forceRemoveSelfLocked();
+ forceRemoveFromServiceLocked();
}
@GuardedBy("mLock")
- void forceRemoveSelfLocked(int clientState) {
- if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
+ void forceRemoveFromServiceLocked(int clientState) {
+ if (sVerbose) Slog.v(TAG, "forceRemoveFromServiceLocked(): " + mPendingSaveUi);
final boolean isPendingSaveUi = isSaveUiPendingLocked();
mPendingSaveUi = null;
- removeSelfLocked();
+ removeFromServiceLocked();
mUi.destroyAll(mPendingSaveUi, this, false);
if (!isPendingSaveUi) {
try {
@@ -4036,28 +4022,28 @@
}
/**
- * Thread-safe version of {@link #removeSelfLocked()}.
+ * Thread-safe version of {@link #removeFromServiceLocked()}.
*/
- private void removeSelf() {
+ private void removeFromService() {
synchronized (mLock) {
- removeSelfLocked();
+ removeFromServiceLocked();
}
}
/**
- * Cleans up this session and remove it from the service, but but only if it does not have a
+ * Destroy this session and remove it from the service, but but only if it does not have a
* pending Save UI.
*/
@GuardedBy("mLock")
- void removeSelfLocked() {
- if (sVerbose) Slog.v(TAG, "removeSelfLocked(" + this.id + "): " + mPendingSaveUi);
+ void removeFromServiceLocked() {
+ if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi);
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#removeSelfLocked() rejected - session: "
+ Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: "
+ id + " destroyed");
return;
}
if (isSaveUiPendingLocked()) {
- Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui");
+ Slog.i(TAG, "removeFromServiceLocked() ignored, waiting for pending save ui");
return;
}
@@ -4139,6 +4125,20 @@
requestLog.addTaggedData(tag, value);
}
+ @GuardedBy("mLock")
+ private void logAugmentedAutofillRequestLocked(int mode,
+ ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted,
+ Boolean isInline) {
+ final String historyItem =
+ "aug:id=" + id + " u=" + uid + " m=" + mode
+ + " a=" + ComponentName.flattenToShortString(mComponentName)
+ + " f=" + focusedId
+ + " s=" + augmentedRemoteServiceName
+ + " w=" + isWhitelisted
+ + " i=" + isInline;
+ mService.getMaster().logRequestLocked(historyItem);
+ }
+
private void wtf(@Nullable Exception e, String fmt, Object...args) {
final String message = String.format(fmt, args);
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 9fc8f0b..ef6dab5 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -56,6 +56,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -100,6 +101,10 @@
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
+ private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
+ "persist.device_config.configuration.disable_rescue_party_factory_reset";
+ // The DeviceConfig namespace containing all RescueParty switches.
+ private static final String NAMESPACE_CONFIGURATION = "configuration";
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
@@ -215,6 +220,10 @@
if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories();
for (int i = 0; i < resetNativeCategories.length; i++) {
+ // Don't let RescueParty reset the namespace for RescueParty switches.
+ if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) {
+ continue;
+ }
DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
resetNativeCategories[i]);
}
@@ -225,8 +234,10 @@
* Get the next rescue level. This indicates the next level of mitigation that may be taken.
*/
private static int getNextRescueLevel() {
+ int maxRescueLevel = SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)
+ ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET;
return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
- LEVEL_NONE, LEVEL_FACTORY_RESET);
+ LEVEL_NONE, maxRescueLevel);
}
/**
@@ -349,12 +360,30 @@
private static void resetDeviceConfig(Context context, int resetMode,
@Nullable String failedPackage) {
if (!shouldPerformScopedResets() || failedPackage == null) {
- DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
+ resetAllAffectedNamespaces(context, resetMode);
} else {
performScopedReset(context, resetMode, failedPackage);
}
}
+ private static void resetAllAffectedNamespaces(Context context, int resetMode) {
+ RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
+ Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet();
+
+ Slog.w(TAG,
+ "Performing reset for all affected namespaces: "
+ + Arrays.toString(allAffectedNamespaces.toArray()));
+ Iterator<String> it = allAffectedNamespaces.iterator();
+ while (it.hasNext()) {
+ String namespace = it.next();
+ // Don't let RescueParty reset the namespace for RescueParty switches.
+ if (NAMESPACE_CONFIGURATION.equals(namespace)) {
+ continue;
+ }
+ DeviceConfig.resetToDefaults(resetMode, namespace);
+ }
+ }
+
private static boolean shouldPerformScopedResets() {
int rescueLevel = MathUtils.constrain(
SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
@@ -367,16 +396,21 @@
RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
failedPackage);
- if (affectedNamespaces == null) {
- DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
- } else {
+ // If we can't find namespaces affected for current package,
+ // skip this round of reset.
+ if (affectedNamespaces != null) {
Slog.w(TAG,
"Performing scoped reset for package: " + failedPackage
+ ", affected namespaces: "
+ Arrays.toString(affectedNamespaces.toArray()));
Iterator<String> it = affectedNamespaces.iterator();
while (it.hasNext()) {
- DeviceConfig.resetToDefaults(resetMode, it.next());
+ String namespace = it.next();
+ // Don't let RescueParty reset the namespace for RescueParty switches.
+ if (NAMESPACE_CONFIGURATION.equals(namespace)) {
+ continue;
+ }
+ DeviceConfig.resetToDefaults(resetMode, namespace);
}
}
}
@@ -514,6 +548,10 @@
return mCallingPackageNamespaceSetMap.get(failedPackage);
}
+ private synchronized Set<String> getAllAffectedNamespaceSet() {
+ return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet());
+ }
+
private synchronized Set<String> getCallingPackagesSet(String namespace) {
return mNamespaceCallingPackageSetMap.get(namespace);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0814937..9f2216d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7745,11 +7745,15 @@
* @return true if the process should exit immediately (WTF is fatal)
*/
@Override
- public boolean handleApplicationWtf(final IBinder app, final String tag, boolean system,
- final ApplicationErrorReport.ParcelableCrashInfo crashInfo, int immediateCallerPid) {
+ public boolean handleApplicationWtf(@Nullable final IBinder app, @Nullable final String tag,
+ boolean system, @NonNull final ApplicationErrorReport.ParcelableCrashInfo crashInfo,
+ int immediateCallerPid) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
+ // Internal callers in RuntimeInit should always generate a crashInfo.
+ Preconditions.checkNotNull(crashInfo);
+
// If this is coming from the system, we could very well have low-level
// system locks held, so we want to do this all asynchronously. And we
// never want this to become fatal, so there is that too.
@@ -7782,14 +7786,15 @@
}
}
- ProcessRecord handleApplicationWtfInner(int callingUid, int callingPid, IBinder app, String tag,
- final ApplicationErrorReport.CrashInfo crashInfo) {
+ ProcessRecord handleApplicationWtfInner(int callingUid, int callingPid, @Nullable IBinder app,
+ @Nullable String tag, @Nullable final ApplicationErrorReport.CrashInfo crashInfo) {
final ProcessRecord r = findAppProcess(app, "WTF");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
EventLogTags.writeAmWtf(UserHandle.getUserId(callingUid), callingPid,
- processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage);
+ processName, r == null ? -1 : r.info.flags, tag,
+ crashInfo == null ? "unknown" : crashInfo.exceptionMessage);
FrameworkStatsLog.write(FrameworkStatsLog.WTF_OCCURRED, callingUid, tag, processName,
callingPid, (r != null) ? r.getProcessClassEnum() : 0);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 6a4ca8d..0b2d4d7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -151,6 +151,8 @@
* run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs
* are rescheduled. A rescheduled sync will get a new jobId.
*
+ * See also {@code SyncManager.md} in the same directory for how app-standby affects sync adapters.
+ *
* @hide
*/
public class SyncManager {
diff --git a/services/core/java/com/android/server/content/SyncManager.md b/services/core/java/com/android/server/content/SyncManager.md
new file mode 100644
index 0000000..8507abd
--- /dev/null
+++ b/services/core/java/com/android/server/content/SyncManager.md
@@ -0,0 +1,122 @@
+# Sync Manager notes
+
+## App-standby and Sync Manager
+
+Android 9 Pie introduced
+["App Standby Buckets"](https://developer.android.com/topic/performance/appstandby), which throttles various things
+including
+[JobScheduler](https://developer.android.com/reference/android/app/job/JobScheduler)
+and [AlarmManager](https://developer.android.com/reference/android/app/AlarmManager),
+[among other things](https://developer.android.com/topic/performance/power/power-details),
+for background applications.
+
+Because SyncManager executes sync operations as JobScheduler jobs, sync operations are subject
+to the same throttling.
+
+However, unlike JobScheduler jobs, any apps (with the proper permission) can schedule a sync
+operation in any other apps using
+[ContentResolver.requestSync()](https://developer.android.com/reference/android/content/ContentResolver#requestSync(android.content.SyncRequest)),
+whch means it's possible for a foreground app to request a sync in another app that is either in the
+background or is not even running.
+For example, when the user hits the refresh button on the Contacts app, it'll
+request sync to all the contacts sync adapters, which are implemented in other packages (and they're
+likely not in the foreground).
+
+Because of this, calls to
+[ContentResolver.requestSync()](https://developer.android.com/reference/android/content/ContentResolver#requestSync(android.content.SyncRequest))
+made by foreground apps are special cased such that the resulting sync operations will be
+exempted from app-standby throttling.
+
+### Two Levels of Exemption
+Specifically, there are two different levels of exemption, depending on the state of the caller:
+1. `ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET`
+2. `ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP`, which is more powerful than 1.
+
+The exemption level is calculated in
+[ContentService.getSyncExemptionAndCleanUpExtrasForCaller()](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/content/ContentService.java?q=%22int%20getSyncExemptionAndCleanUpExtrasForCaller%22&ss=android%2Fplatform%2Fsuperproject),
+which was [implemented slightly differently](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/content/ContentService.java?q=%22int%20getSyncExemptionAndCleanUpExtrasForCaller%22&ss=android%2Fplatform%2Fsuperproject)
+in Android 9, compared to Android 10 and later.
+
+The logic is as follows:
+- When the caller's procstate is `PROCESS_STATE_TOP` or above,
+ meaning if the caller has a foreground activity,
+ `SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP` will be set.
+
+- Otherwise, when the caller's procstate is `PROCESS_STATE_IMPORTANT_FOREGROUND` or above,
+ e.g. when the caller has a foreground service, a service bound by the system of a specific kind,
+ `SYNC_EXEMPTION_PROMOTE_BUCKET` will be set.
+
+- Additionally, on Android 10 and later, when the caller is
+ "UID-active" (but the procstate is below `PROCESS_STATE_TOP`),
+ `SYNC_EXEMPTION_PROMOTE_BUCKET` will be set.
+ This is what happens when the app has just received a high-priority FCM, for example.
+ Temp-allowlist is also used in various other situations.
+
+### Behavior of Each Exemption
+
+The exemptions are tracked in `SyncOperation.syncExemptionFlag`.
+
+- Behavior of `SYNC_EXEMPTION_PROMOTE_BUCKET`
+ - This will add `JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY` to the sync job. This makes the job
+ subject to "ACTIVE" app quota, so minimum deferral will be applied to it.
+
+ - This also reports `AppStandbyController.reportExemptedSyncStart()`, so the package that owns
+ the sync adapter is temporarily put in the "ACTIVE" bucket for the
+ duration of `mExemptedSyncStartTimeoutMillis`, whose default is 10 minutes as of 2020-10-23.
+
+ This will allow the app to access network, even if it has been in the `RARE` bucket
+ (in which case, the system cuts its network access).
+
+ Note if the device is dozing or in battery saver, promoting to the "ACTIVE" bucket will still
+ _not_ give the app network access.
+
+- Behavior of `SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP`
+ - This gives all the perks given by `SYNC_EXEMPTION_PROMOTE_BUCKET`, plus puts the target app
+ in the temp-allowlist (by calling `DeviceIdleInternal.addPowerSaveTempWhitelistApp()`)
+ for the duration of `SyncManagerConstants.getKeyExemptionTempWhitelistDurationInSeconds()`,
+ whose default is 10 minutes.
+
+ Temp-allowlist will grant the app network access even if the device is in doze or in battery
+ saver.
+
+ (However, note that when the device is dozing, sync jobs will not run anyway.)
+
+### How Retries Are Handled
+
+- When a sync operation needs a retry, SyncManager creates a new operation (job) with a back-off
+ (in `SyncManager.maybeRescheduleSync()`). In this case, the new sync operation will inherit
+ `SyncOperation.syncExemptionFlag`, unless the number of retries (not counting the original sync
+ job) is equal to or greater than `SyncManagerConstants.getMaxRetriesWithAppStandbyExemption()`,
+ whose default is 5.
+
+### Special-handling of Pre-installed Packages
+
+- When a content provider is accessed, `AppStandbyController.reportContentProviderUsage()` is
+ triggered, which elevates the standby bucket of the associated sync adapters' packages to `ACTIVE`
+ for the duration of `mSyncAdapterTimeoutMillis`, whose default is 10 minutes, but _only for_
+ pre-installed packages. This is to help pre-installed sync adapters, which often don't have UI,
+ sync properly.
+
+- Also, since Android 11, all the pre-installed apps with no activities will be kept in
+ the `ACTIVE` bucket, which greatly relaxes app-standby throttling. But they're still subject
+ to doze and battery saver.
+
+### Summary
+
+- When the device is dozing, no sync operations will be executed.
+
+- Normally, sync operations are subject to App-Standby, which throttles jobs owned by background
+ apps. Jobs owned by foreground apps are not affected.
+
+- A sync operation requested by a foreground activity will be executed immediately even if the
+ app owning the sync adapter is in RARE bucket, and the device is in battery saver.
+
+- A sync operation requested by a foreground service (or a "bound foreground" service)
+ will be executed immediately even if the app owning the sync adapter is in RARE bucket,
+ *unless* the device is in battery saver.
+
+ Since Android 9 and later, the same thing will happen if the requester is temp-allowlisted (e.g.
+ when it has just received a "high-priority FCM").
+
+- There are certain exemptions for pre-installed apps, but doze and battery saver will still
+ block their sync adapters.
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b10cd12..3ac2185 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1423,17 +1423,18 @@
private Optional<Integer> getViewportType(DisplayDeviceInfo info) {
// Get the corresponding viewport type.
- if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
- return Optional.of(VIEWPORT_INTERNAL);
- } else if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
- return Optional.of(VIEWPORT_EXTERNAL);
- } else if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL
- && !TextUtils.isEmpty(info.uniqueId)) {
- return Optional.of(VIEWPORT_VIRTUAL);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Display " + info + " does not support input device matching.");
- }
+ switch (info.touch) {
+ case DisplayDeviceInfo.TOUCH_INTERNAL:
+ return Optional.of(VIEWPORT_INTERNAL);
+ case DisplayDeviceInfo.TOUCH_EXTERNAL:
+ return Optional.of(VIEWPORT_EXTERNAL);
+ case DisplayDeviceInfo.TOUCH_VIRTUAL:
+ if (!TextUtils.isEmpty(info.uniqueId)) {
+ return Optional.of(VIEWPORT_VIRTUAL);
+ }
+ // fallthrough
+ default:
+ Slog.w(TAG, "Display " + info + " does not support input device matching.");
}
return Optional.empty();
}
@@ -1483,13 +1484,6 @@
return null;
}
- // Only allow a single INTERNAL or EXTERNAL viewport by forcing their uniqueIds
- // to be identical (in particular, empty).
- // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
- if (viewportType != VIEWPORT_VIRTUAL) {
- uniqueId = "";
- }
-
DisplayViewport viewport;
final int count = mViewports.size();
for (int i = 0; i < count; i++) {
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
new file mode 100644
index 0000000..66ec5c4
--- /dev/null
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index e906a7c..90d31f2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.os.Environment;
@@ -68,6 +69,15 @@
private static final int STORAGE_SYSPROPS = 0;
private static final int STORAGE_GLOBAL_SETTINGS = 1;
+ private static final String VALUE_TYPE_STRING = "string";
+ private static final String VALUE_TYPE_INT = "int";
+
+ @StringDef({
+ VALUE_TYPE_STRING,
+ VALUE_TYPE_INT,
+ })
+ private @interface ValueType {}
+
/**
* System property key for Power State Change on Active Source Lost.
*/
@@ -247,17 +257,15 @@
}
}
- private String retrieveValue(@NonNull Setting setting) {
+ private String retrieveValue(@NonNull Setting setting, @NonNull String defaultValue) {
@Storage int storage = getStorage(setting);
String storageKey = getStorageKey(setting);
if (storage == STORAGE_SYSPROPS) {
Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
- return mStorageAdapter.retrieveSystemProperty(storageKey,
- setting.getDefaultValue().getStringValue());
+ return mStorageAdapter.retrieveSystemProperty(storageKey, defaultValue);
} else if (storage == STORAGE_GLOBAL_SETTINGS) {
Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
- return mStorageAdapter.retrieveGlobalSetting(mContext, storageKey,
- setting.getDefaultValue().getStringValue());
+ return mStorageAdapter.retrieveGlobalSetting(mContext, storageKey, defaultValue);
}
return null;
}
@@ -316,13 +324,41 @@
}
/**
- * For a given setting name returns values that are allowed for that setting.
+ * For a given setting name returns true if and only if the value type of that
+ * setting is a string.
*/
- public List<String> getAllowedValues(@NonNull @CecSettingName String name) {
+ public boolean isStringValueType(@NonNull @CecSettingName String name) {
Setting setting = getSetting(name);
if (setting == null) {
throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
}
+ return getSetting(name).getValueType().equals(VALUE_TYPE_STRING);
+ }
+
+ /**
+ * For a given setting name returns true if and only if the value type of that
+ * setting is an int.
+ */
+ public boolean isIntValueType(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ return getSetting(name).getValueType().equals(VALUE_TYPE_INT);
+ }
+
+ /**
+ * For a given setting name returns values that are allowed for that setting (string).
+ */
+ public List<String> getAllowedStringValues(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
List<String> allowedValues = new ArrayList<String>();
for (Value allowedValue : setting.getAllowedValues().getValue()) {
allowedValues.add(allowedValue.getStringValue());
@@ -331,32 +367,92 @@
}
/**
- * For a given setting name returns the default value for that setting.
+ * For a given setting name returns values that are allowed for that setting (string).
*/
- public String getDefaultValue(@NonNull @CecSettingName String name) {
+ public List<Integer> getAllowedIntValues(@NonNull @CecSettingName String name) {
Setting setting = getSetting(name);
if (setting == null) {
throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
}
+ if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
+ List<Integer> allowedValues = new ArrayList<Integer>();
+ for (Value allowedValue : setting.getAllowedValues().getValue()) {
+ allowedValues.add(allowedValue.getIntValue());
+ }
+ return allowedValues;
+ }
+
+ /**
+ * For a given setting name returns the default value for that setting (string).
+ */
+ public String getDefaultStringValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
return getSetting(name).getDefaultValue().getStringValue();
}
/**
- * For a given setting name returns the current value of that setting.
+ * For a given setting name returns the default value for that setting (int).
*/
- public String getValue(@NonNull @CecSettingName String name) {
+ public int getDefaultIntValue(@NonNull @CecSettingName String name) {
Setting setting = getSetting(name);
if (setting == null) {
throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
}
- Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
- return retrieveValue(setting);
+ if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
+ return getSetting(name).getDefaultValue().getIntValue();
}
/**
- * For a given setting name and value sets the current value of that setting.
+ * For a given setting name returns the current value of that setting (string).
*/
- public void setValue(@NonNull @CecSettingName String name, @NonNull String value) {
+ public String getStringValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
+ Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+ return retrieveValue(setting, setting.getDefaultValue().getStringValue());
+ }
+
+ /**
+ * For a given setting name returns the current value of that setting (int).
+ */
+ public int getIntValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a int-type setting.");
+ }
+ Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+ String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue());
+ String value = retrieveValue(setting, defaultValue);
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * For a given setting name and value sets the current value of that setting (string).
+ */
+ public void setStringValue(@NonNull @CecSettingName String name, @NonNull String value) {
Setting setting = getSetting(name);
if (setting == null) {
throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -364,11 +460,38 @@
if (!setting.getUserConfigurable()) {
throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
}
- if (!getAllowedValues(name).contains(value)) {
+ if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a string-type setting.");
+ }
+ if (!getAllowedStringValues(name).contains(value)) {
throw new IllegalArgumentException("Invalid CEC setting '" + name
+ "' value: '" + value + "'.");
}
Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
storeValue(setting, value);
}
+
+ /**
+ * For a given setting name and value sets the current value of that setting (int).
+ */
+ public void setIntValue(@NonNull @CecSettingName String name, int value) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getUserConfigurable()) {
+ throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
+ }
+ if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
+ throw new IllegalArgumentException("Setting '" + name
+ + "' is not a int-type setting.");
+ }
+ if (!getAllowedIntValues(name).contains(value)) {
+ throw new IllegalArgumentException("Invalid CEC setting '" + name
+ + "' value: '" + value + "'.");
+ }
+ Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+ storeValue(setting, Integer.toString(value));
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index fe4fd38..9dc0079 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -495,17 +495,17 @@
private byte[] getSupportedShortAudioDescriptorsFromConfig(
List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) {
DeviceConfig deviceConfigToUse = null;
+ String audioDeviceName = SystemProperties.get(
+ Constants.PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT,
+ "VX_AUDIO_DEVICE_IN_HDMI_ARC");
for (DeviceConfig device : deviceConfig) {
- // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
- if (device.name.equals("VX_AUDIO_DEVICE_IN_HDMI_ARC")) {
+ if (device.name.equals(audioDeviceName)) {
deviceConfigToUse = device;
break;
}
}
if (deviceConfigToUse == null) {
- // TODO(amyjojo) use PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT to get the audio device name
- Slog.w(TAG, "sadConfig.xml does not have required device info for "
- + "VX_AUDIO_DEVICE_IN_HDMI_ARC");
+ Slog.w(TAG, "sadConfig.xml does not have required device info for " + audioDeviceName);
return new byte[0];
}
HashMap<Integer, byte[]> map = new HashMap<>();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index da4c6f1..cc0b882 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2242,9 +2242,15 @@
List<String> allSettings = hdmiCecConfig.getAllSettings();
Set<String> userSettings = new HashSet<>(hdmiCecConfig.getUserSettings());
for (String setting : allSettings) {
- pw.println(setting + ": " + hdmiCecConfig.getValue(setting)
- + " (default: " + hdmiCecConfig.getDefaultValue(setting) + ")"
- + (userSettings.contains(setting) ? " [modifiable]" : ""));
+ if (hdmiCecConfig.isStringValueType(setting)) {
+ pw.println(setting + " (string): " + hdmiCecConfig.getStringValue(setting)
+ + " (default: " + hdmiCecConfig.getDefaultStringValue(setting) + ")"
+ + (userSettings.contains(setting) ? " [modifiable]" : ""));
+ } else if (hdmiCecConfig.isIntValueType(setting)) {
+ pw.println(setting + " (int): " + hdmiCecConfig.getIntValue(setting)
+ + " (default: " + hdmiCecConfig.getDefaultIntValue(setting) + ")"
+ + (userSettings.contains(setting) ? " [modifiable]" : ""));
+ }
}
pw.decreaseIndent();
@@ -2273,33 +2279,68 @@
}
@Override
- public List<String> getAllowedCecSettingValues(String name) {
+ public List<String> getAllowedCecSettingStringValues(String name) {
enforceAccessPermission();
long token = Binder.clearCallingIdentity();
try {
- return HdmiControlService.this.getHdmiCecConfig().getAllowedValues(name);
+ return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public String getCecSettingValue(String name) {
+ public int[] getAllowedCecSettingIntValues(String name) {
enforceAccessPermission();
long token = Binder.clearCallingIdentity();
try {
- return HdmiControlService.this.getHdmiCecConfig().getValue(name);
+ List<Integer> allowedValues =
+ HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
+ return allowedValues.stream().mapToInt(i->i).toArray();
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void setCecSettingValue(String name, String value) {
+ public String getCecSettingStringValue(String name) {
enforceAccessPermission();
long token = Binder.clearCallingIdentity();
try {
- HdmiControlService.this.getHdmiCecConfig().setValue(name, value);
+ return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setCecSettingStringValue(String name, String value) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public int getCecSettingIntValue(String name) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setCecSettingIntValue(String name, int value) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9a60afb..683a4b6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -15,6 +15,7 @@
package com.android.server.inputmethod;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.server.inputmethod.InputMethodManagerServiceProto.ACCESSIBILITY_REQUESTING_NO_SOFT_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION;
import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD;
@@ -110,6 +111,7 @@
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
@@ -3106,6 +3108,7 @@
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
if (!calledFromValidUserLocked()) {
@@ -3133,6 +3136,7 @@
SoftInputShowHideReason.SHOW_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
@@ -3225,6 +3229,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput");
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
// We need to check if this is the current client with
@@ -3248,6 +3253,7 @@
SoftInputShowHideReason.HIDE_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
@@ -3310,43 +3316,52 @@
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
}
- final int callingUserId = UserHandle.getCallingUserId();
- final int userId;
- if (attribute != null && attribute.targetInputMethodUser != null
- && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
- mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "Using EditorInfo.targetInputMethodUser requires INTERACT_ACROSS_USERS_FULL.");
- userId = attribute.targetInputMethodUser.getIdentifier();
- if (!mUserManagerInternal.isUserRunning(userId)) {
- // There is a chance that we hit here because of race condition. Let's just return
- // an error code instead of crashing the caller process, which at least has
- // INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important process.
- Slog.e(TAG, "User #" + userId + " is not running.");
- return InputBindResult.INVALID_USER;
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+ "IMMS.startInputOrWindowGainedFocus");
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int userId;
+ if (attribute != null && attribute.targetInputMethodUser != null
+ && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
+ mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Using EditorInfo.targetInputMethodUser requires"
+ + " INTERACT_ACROSS_USERS_FULL.");
+ userId = attribute.targetInputMethodUser.getIdentifier();
+ if (!mUserManagerInternal.isUserRunning(userId)) {
+ // There is a chance that we hit here because of race condition. Let's just
+ // return an error code instead of crashing the caller process, which at least
+ // has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important
+ // process.
+ Slog.e(TAG, "User #" + userId + " is not running.");
+ return InputBindResult.INVALID_USER;
+ }
+ } else {
+ userId = callingUserId;
}
- } else {
- userId = callingUserId;
- }
- final InputBindResult result;
- synchronized (mMethodMap) {
- final long ident = Binder.clearCallingIdentity();
- try {
- result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
- windowToken, startInputFlags, softInputMode, windowFlags, attribute,
- inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ final InputBindResult result;
+ synchronized (mMethodMap) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
+ windowToken, startInputFlags, softInputMode, windowFlags, attribute,
+ inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
+ if (result == null) {
+ // This must never happen, but just in case.
+ Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+ + InputMethodDebug.startInputReasonToString(startInputReason)
+ + " windowFlags=#" + Integer.toHexString(windowFlags)
+ + " editorInfo=" + attribute);
+ return InputBindResult.NULL;
+ }
+
+ return result;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (result == null) {
- // This must never happen, but just in case.
- Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
- + InputMethodDebug.startInputReasonToString(startInputReason)
- + " windowFlags=#" + Integer.toHexString(windowFlags)
- + " editorInfo=" + attribute);
- return InputBindResult.NULL;
- }
- return result;
}
@NonNull
@@ -4124,6 +4139,7 @@
@BinderThread
private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
synchronized (mMethodMap) {
if (!calledWithValidTokenLocked(token)) {
return;
@@ -4145,6 +4161,7 @@
mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken));
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
@@ -4172,6 +4189,7 @@
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
synchronized (mMethodMap) {
if (!calledWithValidTokenLocked(token)) {
return;
@@ -4186,10 +4204,12 @@
Binder.restoreCallingIdentity(ident);
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
synchronized (mMethodMap) {
if (!calledWithValidTokenLocked(token)) {
return;
@@ -4202,6 +4222,7 @@
Binder.restoreCallingIdentity(ident);
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
void setEnabledSessionInMainThread(SessionState session) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9c48d23..6874f23 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -21,10 +21,10 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationManager.BLOCK_PENDING_INTENT_SYSTEM_API_USAGE;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
-import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE;
import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -597,14 +597,15 @@
// simplest to ensure these apis are simply never set for pending intent requests. the same
// does not apply for listener requests since those will have the process (including the
// listener) killed on permission removal
- boolean usesSystemApi = request.isLowPower()
- || request.isHiddenFromAppOps()
- || request.isLocationSettingsIgnored()
- || !request.getWorkSource().isEmpty();
- if (usesSystemApi
- && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
- throw new SecurityException(
- "PendingIntent location requests may not use system APIs: " + request);
+ if (isChangeEnabled(BLOCK_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
+ boolean usesSystemApi = request.isLowPower()
+ || request.isHiddenFromAppOps()
+ || request.isLocationSettingsIgnored()
+ || !request.getWorkSource().isEmpty();
+ if (usesSystemApi) {
+ throw new SecurityException(
+ "PendingIntent location requests may not use system APIs: " + request);
+ }
}
request = validateLocationRequest(request, identity);
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 7a59cba..c23bd85 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -296,15 +296,15 @@
@Nullable String attributionTag) {
LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);
- CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName,
attributionTag, AppOpsManager.toReceiverId(pendingIntent));
- final long identity = Binder.clearCallingIdentity();
+ final long ident = Binder.clearCallingIdentity();
try {
putRegistration(new GeofenceKey(pendingIntent, geofence),
- new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
+ new GeofenceRegistration(geofence, identity, pendingIntent));
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelLogger.java b/services/core/java/com/android/server/notification/NotificationChannelLogger.java
index 51faac7..36eec26 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelLogger.java
@@ -215,6 +215,13 @@
}
/**
+ * @return Small hash of the conversation ID, if present, or 0 otherwise.
+ */
+ static int getConversationIdHash(@NonNull NotificationChannel channel) {
+ return SmallHash.hash(channel.getConversationId());
+ }
+
+ /**
* @return Small hash of the channel ID, if present, or 0 otherwise.
*/
static int getIdHash(@NonNull NotificationChannelGroup group) {
diff --git a/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
index fd3dd56..5a7bc48 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java
@@ -41,7 +41,12 @@
/* String package_name */ pkg,
/* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channel),
/* int old_importance*/ oldImportance,
- /* int importance*/ newImportance);
+ /* int importance*/ newImportance,
+ /* bool is_conversation */ channel.isConversation(),
+ /* int32 conversation_id_hash */
+ NotificationChannelLogger.getConversationIdHash(channel),
+ /* bool is_conversation_demoted */ channel.isDemoted(),
+ /* bool is_conversation_priority */ channel.isImportantConversation());
}
@Override
@@ -53,7 +58,11 @@
/* String package_name */ pkg,
/* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channelGroup),
/* int old_importance*/ NotificationChannelLogger.getImportance(wasBlocked),
- /* int importance*/ NotificationChannelLogger.getImportance(channelGroup));
+ /* int importance*/ NotificationChannelLogger.getImportance(channelGroup),
+ /* bool is_conversation */ false,
+ /* int32 conversation_id_hash */ 0,
+ /* bool is_conversation_demoted */ false,
+ /* bool is_conversation_priority */ false);
}
@Override
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 72803ac..780c522 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -101,21 +101,18 @@
if (DEBUG) {
Slog.i(TAG, "received package commit event");
}
+ final boolean startableStateChanged;
synchronized (mLock) {
- if (!mStartableState.isStartable()) {
- mStartableState.adoptNewStartableStateLocked(true);
- }
+ startableStateChanged = mStartableState.adoptNewStartableStateLocked(true);
if (!isIncremental) {
updateProgressLocked(1);
}
}
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportStartableState,
- IncrementalStates.this).recycleOnUse());
+ if (startableStateChanged) {
+ onStartableStateChanged();
+ }
if (!isIncremental) {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportFullyLoaded,
- IncrementalStates.this).recycleOnUse());
+ onLoadingStateChanged();
}
}
@@ -131,8 +128,7 @@
synchronized (mLock) {
if (mStartableState.isStartable() && mLoadingState.isLoading()) {
// Changing from startable -> unstartable only if app is still loading.
- mStartableState.adoptNewStartableStateLocked(false);
- startableStateChanged = true;
+ startableStateChanged = mStartableState.adoptNewStartableStateLocked(false);
} else {
// If the app is fully loaded, the crash or ANR is caused by the app itself, so
// we do not change the startable state.
@@ -140,12 +136,15 @@
}
}
if (startableStateChanged) {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportStartableState,
- IncrementalStates.this).recycleOnUse());
+ onStartableStateChanged();
}
}
+ private void onStartableStateChanged() {
+ // Disable startable state broadcasts
+ // TODO(b/171920377): completely remove unstartable state.
+ }
+
private void reportStartableState() {
final Callback callback;
final boolean startable;
@@ -165,6 +164,12 @@
}
}
+ private void onLoadingStateChanged() {
+ mHandler.post(PooledLambda.obtainRunnable(
+ IncrementalStates::reportFullyLoaded,
+ IncrementalStates.this).recycleOnUse());
+ }
+
private void reportFullyLoaded() {
final Callback callback;
synchronized (mLock) {
@@ -178,23 +183,20 @@
private class StatusConsumer implements Consumer<Integer> {
@Override
public void accept(Integer storageStatus) {
- final boolean oldState, newState;
+ final boolean startableStateChanged;
synchronized (mLock) {
if (!mLoadingState.isLoading()) {
// Do nothing if the package is already fully loaded
return;
}
- oldState = mStartableState.isStartable();
mStorageHealthStatus = storageStatus;
- updateStartableStateLocked();
- newState = mStartableState.isStartable();
+ startableStateChanged = updateStartableStateLocked();
}
- if (oldState != newState) {
- mHandler.post(PooledLambda.obtainRunnable(IncrementalStates::reportStartableState,
- IncrementalStates.this).recycleOnUse());
+ if (startableStateChanged) {
+ onStartableStateChanged();
}
}
- };
+ }
/**
* By calling this method, the caller indicates that there issues with the Incremental
@@ -239,14 +241,10 @@
newStartableState = mStartableState.isStartable();
}
if (!newLoadingState) {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportFullyLoaded,
- IncrementalStates.this).recycleOnUse());
+ onLoadingStateChanged();
}
if (newStartableState != oldStartableState) {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportStartableState,
- IncrementalStates.this).recycleOnUse());
+ onStartableStateChanged();
}
}
@@ -284,8 +282,9 @@
* health
* status. If the next state is different from the current state, proceed with state
* change.
+ * @return True if the new startable state is different from the old one.
*/
- private void updateStartableStateLocked() {
+ private boolean updateStartableStateLocked() {
final boolean currentState = mStartableState.isStartable();
boolean nextState = currentState;
if (!currentState) {
@@ -302,9 +301,9 @@
}
}
if (nextState == currentState) {
- return;
+ return false;
}
- mStartableState.adoptNewStartableStateLocked(nextState);
+ return mStartableState.adoptNewStartableStateLocked(nextState);
}
private void updateProgressLocked(float progress) {
@@ -343,12 +342,30 @@
return mUnstartableReason;
}
- public void adoptNewStartableStateLocked(boolean nextState) {
+ /**
+ * Adopt new startable state if it is different from the current state.
+ * @param nextState True if startable, false if unstartable.
+ * @return True if the state has changed, false otherwise.
+ */
+ public boolean adoptNewStartableStateLocked(boolean nextState) {
+ if (mIsStartable == nextState) {
+ return false;
+ }
+ if (!nextState) {
+ // Do nothing if the next state is "unstartable"; keep package always startable.
+ // TODO(b/171920377): completely remove unstartable state.
+ if (DEBUG) {
+ Slog.i(TAG, "Attempting to set startable state to false. Abort.");
+ }
+ return false;
+ }
if (DEBUG) {
- Slog.i(TAG, "startable state changed from " + mIsStartable + " to " + nextState);
+ Slog.i(TAG,
+ "startable state changed from " + mIsStartable + " to " + nextState);
}
mIsStartable = nextState;
mUnstartableReason = getUnstartableReasonLocked();
+ return true;
}
private int getUnstartableReasonLocked() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b3f49ad..203321e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -16478,8 +16478,8 @@
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs =
INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
- mIncrementalManager.registerHealthListener(codePath,
- new StorageHealthCheckParams(), incrementalHealthListener);
+ mIncrementalManager.registerHealthListener(codePath, healthCheckParams,
+ incrementalHealthListener);
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
@@ -19605,8 +19605,6 @@
if (installed) {
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
}
-
- writeRuntimePermissionsForUserLPrTEMP(userId, false);
}
// Regardless of writeSettings we need to ensure that this restriction
// state propagation is persisted
@@ -25751,8 +25749,9 @@
@Override
public void writePermissionSettings(int[] userIds, boolean async) {
synchronized (mLock) {
+ mPermissionManager.writeLegacyPermissionStateTEMP();
for (int userId : userIds) {
- writeRuntimePermissionsForUserLPrTEMP(userId, !async);
+ mSettings.writeRuntimePermissionsForUserLPr(userId, !async);
}
}
}
@@ -26401,17 +26400,6 @@
mSettings.writeLPr();
}
- /**
- * Temporary method that wraps mSettings.writeRuntimePermissionsForUserLPr() and calls
- * mPermissionManager.writeLegacyPermissionStateTEMP() beforehand.
- *
- * TODO(zhanghai): This should be removed once we finish migration of permission storage.
- */
- private void writeRuntimePermissionsForUserLPrTEMP(@UserIdInt int userId, boolean async) {
- mPermissionManager.writeLegacyPermissionStateTEMP();
- mSettings.writeRuntimePermissionsForUserLPr(userId, async);
- }
-
@Override
public IBinder getHoldLockToken() {
if (!Build.IS_DEBUGGABLE) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7f29cd9..24082b8 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3993,7 +3993,7 @@
@Override
public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
Slog.i(LOG_TAG, "removeUserOrSetEphemeral u" + userId);
- checkManageUsersPermission("Only the system can remove users");
+ checkManageOrCreateUsersPermission("Only the system can remove users");
final String restriction = getUserRemovalRestriction(userId);
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8b677a9..fcba5ce 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2429,6 +2429,15 @@
wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
view = win.getDecorView();
+ // Ignore to show splash screen if the decorView is not opaque.
+ if (!view.isOpaque()) {
+ if (DEBUG_SPLASH_SCREEN) {
+ Slog.d(TAG, "addSplashScreen: the view of " + packageName
+ + " is not opaque, cancel it");
+ }
+ return null;
+ }
+
if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
+ packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f65a5024..19b13c1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -194,7 +194,7 @@
import static com.android.server.wm.Task.ActivityState.STARTED;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -417,7 +417,7 @@
// mOccludesParent field.
final boolean hasWallpaper;
// Input application handle used by the input dispatcher.
- final InputApplicationHandle mInputApplicationHandle;
+ private InputApplicationHandle mInputApplicationHandle;
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
@@ -1506,7 +1506,6 @@
info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName;
- mInputApplicationHandle = new InputApplicationHandle(appToken);
intent = _intent;
// If the class name in the intent doesn't match that of the target, this is probably an
@@ -1693,6 +1692,21 @@
return lockTaskLaunchMode;
}
+ @NonNull InputApplicationHandle getInputApplicationHandle(boolean update) {
+ if (mInputApplicationHandle == null) {
+ mInputApplicationHandle = new InputApplicationHandle(appToken, toString(),
+ mInputDispatchingTimeoutMillis);
+ } else if (update) {
+ final String name = toString();
+ if (mInputDispatchingTimeoutMillis != mInputApplicationHandle.dispatchingTimeoutMillis
+ || !name.equals(mInputApplicationHandle.name)) {
+ mInputApplicationHandle = new InputApplicationHandle(appToken, name,
+ mInputDispatchingTimeoutMillis);
+ }
+ }
+ return mInputApplicationHandle;
+ }
+
@Override
ActivityRecord asActivityRecord() {
// I am an activity record!
@@ -4881,7 +4895,7 @@
*/
private boolean shouldBeResumed(ActivityRecord activeActivity) {
return shouldMakeActive(activeActivity) && isFocusable()
- && getTask().getVisibility(activeActivity) == STACK_VISIBILITY_VISIBLE
+ && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
&& canResumeByCompat();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a068d2b..17209eb 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -50,12 +50,12 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -63,17 +63,17 @@
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.TAG_CLEANUP;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -158,7 +158,7 @@
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
+ private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
@@ -526,7 +526,7 @@
int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
while (mRecentTasks.containsTaskId(candidateTaskId, userId)
|| mRootWindowContainer.anyTaskForId(
- candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
+ candidateTaskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS) != null) {
candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
if (candidateTaskId == currentTaskId) {
// Something wrong!
@@ -1360,8 +1360,8 @@
if (stack != currentStack) {
moveHomeStackToFrontIfNeeded(flags, stack.getDisplayArea(), reason);
- task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
- reason);
+ task.reparent(stack, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT, !ANIMATE,
+ DEFER_RESUME, reason);
currentStack = stack;
reparented = true;
// task.reparent() should already placed the task on top,
@@ -1385,7 +1385,7 @@
currentStack.moveTaskToFront(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
- if (DEBUG_STACK) Slog.d(TAG_STACK,
+ if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
"findTaskToMoveToFront: moved to front of stack=" + currentStack);
handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
@@ -1502,7 +1502,7 @@
boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
String reason) {
final Task task =
- mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task != null) {
removeTask(task, killProcess, removeFromRecents, reason);
return true;
@@ -2478,7 +2478,7 @@
mService.deferWindowLayout();
try {
task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
if (task == null) {
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 910a1a2..33819a9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -75,7 +75,7 @@
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
@@ -1665,11 +1665,6 @@
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
setNewTask(taskToAffiliate);
- if (mService.getLockTaskController().isLockTaskModeViolation(
- mStartActivity.getTask())) {
- Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
- return START_RETURN_LOCK_TASK_MODE_VIOLATION;
- }
} else if (mAddingToTask) {
addOrReparentStartingActivity(targetTask, "adding to task");
}
@@ -1848,10 +1843,17 @@
final boolean isNewClearTask =
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- if (!newTask && mService.getLockTaskController().isLockTaskModeViolation(targetTask,
- isNewClearTask)) {
- Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
- return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ if (!newTask) {
+ if (mService.getLockTaskController().isLockTaskModeViolation(targetTask,
+ isNewClearTask)) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+ return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
+ } else {
+ if (mService.getLockTaskController().isNewTaskLockTaskModeViolation(mStartActivity)) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+ return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
}
return START_SUCCESS;
@@ -2530,8 +2532,8 @@
"bringingFoundTaskToFront");
mMovedToFront = !isSplitScreenTopStack;
} else {
- intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
- DEFER_RESUME, "reparentToTargetStack");
+ intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ ANIMATE, DEFER_RESUME, "reparentToTargetStack");
mMovedToFront = true;
}
mOptions = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
index b5675a9..33d1b44 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
@@ -45,7 +45,7 @@
static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
- static final boolean DEBUG_STACK = DEBUG_ALL || false;
+ static final boolean DEBUG_ROOT_TASK = DEBUG_ALL || false;
public static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
@@ -73,7 +73,7 @@
static final String POSTFIX_PAUSE = APPEND_CATEGORY_NAME ? "_Pause" : "";
static final String POSTFIX_RECENTS = APPEND_CATEGORY_NAME ? "_Recents" : "";
static final String POSTFIX_SAVED_STATE = APPEND_CATEGORY_NAME ? "_SavedState" : "";
- static final String POSTFIX_STACK = APPEND_CATEGORY_NAME ? "_Stack" : "";
+ static final String POSTFIX_ROOT_TASK = APPEND_CATEGORY_NAME ? "_RootTask" : "";
static final String POSTFIX_STATES = APPEND_CATEGORY_NAME ? "_States" : "";
public static final String POSTFIX_SWITCH = APPEND_CATEGORY_NAME ? "_Switch" : "";
static final String POSTFIX_TASKS = APPEND_CATEGORY_NAME ? "_Tasks" : "";
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 581b367..73c4713 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -106,7 +106,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -117,14 +117,14 @@
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.Task.ActivityState.DESTROYED;
import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -306,7 +306,7 @@
*/
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
- static final String TAG_STACK = TAG + POSTFIX_STACK;
+ static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
@@ -2228,7 +2228,7 @@
try {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
return;
}
@@ -2266,7 +2266,7 @@
final long ident = Binder.clearCallingIdentity();
try {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
return false;
@@ -2374,7 +2374,7 @@
try {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
return rect;
@@ -2397,7 +2397,7 @@
enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_TASKS, "getTaskDescription()");
final Task tr = mRootWindowContainer.anyTaskForId(id,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (tr != null) {
return tr.getTaskDescription();
}
@@ -2418,7 +2418,7 @@
return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
}
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
return false;
@@ -2789,8 +2789,8 @@
throw new IllegalArgumentException("moveTaskToRootTask: Attempt to move task "
+ taskId + " to rootTask " + rootTaskId);
}
- task.reparent(rootTask, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
- "moveTaskToRootTask");
+ task.reparent(rootTask, toTop, REPARENT_KEEP_ROOT_TASK_AT_FRONT, ANIMATE,
+ !DEFER_RESUME, "moveTaskToRootTask");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2834,7 +2834,7 @@
}
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
return false;
@@ -3013,7 +3013,7 @@
try {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
return;
}
@@ -3361,7 +3361,7 @@
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(
- taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
return;
@@ -3377,7 +3377,7 @@
try {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return false;
@@ -4395,7 +4395,7 @@
try {
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
+ MATCH_ATTACHED_TASK_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
return;
@@ -4423,7 +4423,7 @@
final Task task;
synchronized (mGlobalLock) {
task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 3e79879..fbbda59 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -17,7 +17,7 @@
package com.android.server.wm;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import android.app.ActivityManager;
import android.app.IAppTask;
@@ -79,7 +79,7 @@
final long origId = Binder.clearCallingIdentity();
try {
Task task = mService.mRootWindowContainer.anyTaskForId(mTaskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
@@ -136,7 +136,7 @@
IApplicationThread appThread;
synchronized (mService.mGlobalLock) {
task = mService.mRootWindowContainer.anyTaskForId(mTaskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
@@ -165,7 +165,7 @@
final long origId = Binder.clearCallingIdentity();
try {
Task task = mService.mRootWindowContainer.anyTaskForId(mTaskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6453ddf..3a4eb09 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -88,7 +88,6 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CAN_SHOW_IME;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
@@ -238,7 +237,6 @@
*/
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
/** The default scaling mode that scales content automatically. */
static final int FORCE_SCALING_MODE_AUTO = 0;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 2ea4b57..8c80205 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -277,9 +277,8 @@
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
- mDragApplicationHandle = new InputApplicationHandle(new Binder());
- mDragApplicationHandle.name = "drag";
- mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ mDragApplicationHandle = new InputApplicationHandle(new Binder(), "drag",
+ DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 3b89a24..b08d6e1 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -175,11 +175,11 @@
InputApplicationHandle getApplicationHandle() {
if (mHostWindowState == null
- || mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) {
+ || mHostWindowState.mInputWindowHandle.getInputApplicationHandle() == null) {
return null;
}
return new InputApplicationHandle(
- mHostWindowState.mInputWindowHandle.inputApplicationHandle);
+ mHostWindowState.mInputWindowHandle.getInputApplicationHandle());
}
InputChannel openInputChannel() {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 0813b4f..818d96c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN;
+import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
import android.view.WindowInsets;
@@ -79,6 +82,7 @@
ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
target.getWindow() != null ? target.getWindow().getName() : "");
target.showInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
if (target != mImeTargetFromIme && mImeTargetFromIme != null) {
ProtoLog.w(WM_DEBUG_IME,
"showInsets(ime) was requested by different window: %s ",
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index edb5e85..e35621a 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -63,9 +63,8 @@
mClientChannel.copyTo(inputChannel);
}
- mApplicationHandle = new InputApplicationHandle(new Binder());
- mApplicationHandle.name = name;
- mApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ mApplicationHandle = new InputApplicationHandle(new Binder(), name,
+ DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
@@ -160,9 +159,11 @@
public void binderDied() {
synchronized (mService.getWindowManagerLock()) {
// Clean up the input consumer
- final InputMonitor inputMonitor =
- mService.mRoot.getDisplayContent(mWindowHandle.displayId).getInputMonitor();
- inputMonitor.destroyInputConsumer(mName);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mWindowHandle.displayId);
+ if (dc == null) {
+ return;
+ }
+ dc.getInputMonitor().destroyInputConsumer(mName);
unlinkFromDeathRecipient();
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4a54196..43c4741 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -48,6 +48,7 @@
import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -57,12 +58,12 @@
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -81,7 +82,7 @@
private boolean mUpdateInputWindowsImmediately;
private boolean mDisableWallpaperTouchEvents;
- private final Rect mTmpRect = new Rect();
+ private final Region mTmpRegion = new Region();
private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
private final int mDisplayId;
@@ -276,66 +277,66 @@
addInputConsumer(name, consumer);
}
-
- void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
- final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean focusable, final boolean hasWallpaper) {
+ @VisibleForTesting
+ void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle,
+ final WindowState w) {
// Add a window to our list of input windows.
- inputWindowHandle.name = child.toString();
- flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
- inputWindowHandle.layoutParamsFlags = flags;
- inputWindowHandle.layoutParamsType = type;
- inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
- inputWindowHandle.visible = isVisible;
- inputWindowHandle.focusable = focusable;
- inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode();
- inputWindowHandle.hasWallpaper = hasWallpaper;
- inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
- inputWindowHandle.ownerPid = child.mSession.mPid;
- inputWindowHandle.ownerUid = child.mSession.mUid;
- inputWindowHandle.packageName = child.getOwningPackage();
- inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
- inputWindowHandle.displayId = child.getDisplayId();
+ inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null
+ ? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null);
+ inputWindowHandle.setToken(w.mInputChannelToken);
+ inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
+ inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
+ inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures);
+ inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
+ inputWindowHandle.setVisible(w.isVisible());
- final Rect frame = child.getFrame();
- inputWindowHandle.frameLeft = frame.left;
- inputWindowHandle.frameTop = frame.top;
- inputWindowHandle.frameRight = frame.right;
- inputWindowHandle.frameBottom = frame.bottom;
+ final boolean focusable = w.canReceiveKeys()
+ && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
+ inputWindowHandle.setFocusable(focusable);
+
+ final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w)
+ && !mService.mPolicy.isKeyguardShowing()
+ && !mDisableWallpaperTouchEvents;
+ inputWindowHandle.setHasWallpaper(hasWallpaper);
+
+ final Rect frame = w.getFrame();
+ inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom);
// Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here.
- inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+ inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left);
- /**
- * If the window is in a TaskManaged by a TaskOrganizer then most cropping
- * will be applied using the SurfaceControl hierarchy from the Organizer.
- * This means we need to make sure that these changes in crop are reflected
- * in the input windows, and so ensure this flag is set so that
- * the input crop always reflects the surface hierarchy.
- *
- * TODO(b/168252846): we have some issues with modal-windows, so we need to
- * cross that bridge now that we organize full-screen Tasks.
- */
- if (child.getTask() != null
- && child.getTask().isOrganized()
- && child.getTask().getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
- inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
+ // If we are scaling the window, input coordinates need to be inversely scaled to map from
+ // what is on screen to what is actually being touched in the UI.
+ inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
+
+ final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags);
+ inputWindowHandle.setTouchableRegion(mTmpRegion);
+ inputWindowHandle.setLayoutParamsFlags(flags);
+
+ boolean useSurfaceCrop = false;
+ final Task task = w.getTask();
+ if (task != null) {
+ if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ // If the window is in a TaskManaged by a TaskOrganizer then most cropping will
+ // be applied using the SurfaceControl hierarchy from the Organizer. This means
+ // we need to make sure that these changes in crop are reflected in the input
+ // windows, and so ensure this flag is set so that the input crop always reflects
+ // the surface hierarchy.
+ // TODO(b/168252846): we have some issues with modal-windows, so we need to cross
+ // that bridge now that we organize full-screen Tasks.
+ inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
+ useSurfaceCrop = true;
+ } else if (task.cropWindowsToStackBounds() && !w.inFreeformWindowingMode()) {
+ inputWindowHandle.replaceTouchableRegionWithCrop(
+ task.getRootTask().getSurfaceControl());
+ useSurfaceCrop = true;
+ }
}
-
- if (child.mGlobalScale != 1) {
- // If we are scaling the window, input coordinates need
- // to be inversely scaled to map from what is on screen
- // to what is actually being touched in the UI.
- inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
- } else {
- inputWindowHandle.scaleFactor = 1;
- }
-
- if (DEBUG_INPUT) {
- Slog.d(TAG_WM, "addInputWindowHandle: "
- + child + ", " + inputWindowHandle);
+ if (!useSurfaceCrop) {
+ inputWindowHandle.setReplaceTouchableRegionWithCrop(false);
+ inputWindowHandle.setTouchableRegionCrop(null);
}
}
@@ -401,15 +402,8 @@
public void setFocusedAppLw(ActivityRecord newApp) {
// Focused app has changed.
- if (newApp == null) {
- mService.mInputManager.setFocusedApplication(mDisplayId, null);
- } else {
- final InputApplicationHandle handle = newApp.mInputApplicationHandle;
- handle.name = newApp.toString();
- handle.dispatchingTimeoutMillis = newApp.mInputDispatchingTimeoutMillis;
-
- mService.mInputManager.setFocusedApplication(mDisplayId, handle);
- }
+ mService.mInputManager.setFocusedApplication(mDisplayId,
+ newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
}
public void pauseDispatchingLw(WindowToken window) {
@@ -456,10 +450,6 @@
private boolean mAddRecentsAnimationInputConsumerHandle;
boolean mInDrag;
- WallpaperController mWallpaperController;
-
- // An invalid window handle that tells SurfaceFlinger not update the input info.
- final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId);
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -474,10 +464,8 @@
mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
- mTmpRect.setEmpty();
mDisableWallpaperTouchEvents = false;
mInDrag = inDrag;
- mWallpaperController = mDisplayContent.mWallpaperController;
resetInputConsumers(mInputTransaction);
@@ -499,7 +487,7 @@
}
final WindowState focus = mDisplayContent.mCurrentFocus;
- if (focus == null || focus.mInputWindowHandle.token == null) {
+ if (focus == null || focus.mInputChannelToken == null) {
mDisplayContent.mLastRequestedFocus = focus;
return;
}
@@ -510,7 +498,7 @@
return;
}
- mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId);
+ mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId);
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Focus request " + focus, "reason=UpdateInputWindows");
mDisplayContent.mLastRequestedFocus = focus;
@@ -519,33 +507,26 @@
@Override
public void accept(WindowState w) {
- final InputChannel inputChannel = w.mInputChannel;
- final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
+ final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
- final int type = w.mAttrs.type;
- final boolean isVisible = w.isVisibleLw();
- if (inputChannel == null || inputWindowHandle == null || w.mRemoved
+ if (w.mInputChannelToken == null || w.mRemoved
|| (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
if (w.mWinAnimator.hasSurface()) {
// Assign an InputInfo with type to the overlay window which can't receive input
// event. This is used to omit Surfaces from occlusion detection.
- populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible);
- mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.mSurfaceControl,
- mInvalidInputWindow);
+ populateOverlayInputInfo(inputWindowHandle, w.isVisible());
+ setInputWindowInfoIfNeeded(mInputTransaction,
+ w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
return;
}
// Skip this window because it cannot possibly receive input.
return;
}
- final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
- final boolean focusable = w.canReceiveKeys()
- && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
@@ -584,47 +565,53 @@
if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
mDisableWallpaperTouchEvents = true;
}
- final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w)
- && !mService.mPolicy.isKeyguardShowing()
- && !mDisableWallpaperTouchEvents;
// If there's a drag in progress and 'child' is a potential drop target,
// make sure it's been told about the drag
- if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
+ if (mInDrag && w.isVisible() && w.getDisplayContent().isDefaultDisplay) {
mService.mDragDropController.sendDragStartedIfNeededLocked(w);
}
- populateInputWindowHandle(
- inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper);
-
// register key interception info
- mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
+ mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken,
w.getKeyInterceptionInfo());
if (w.mWinAnimator.hasSurface()) {
- mInputTransaction.setInputWindowInfo(
+ populateInputWindowHandle(inputWindowHandle, w);
+ setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
}
}
}
+ @VisibleForTesting
+ static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc,
+ InputWindowHandleWrapper inputWindowHandle) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle);
+ }
+ if (inputWindowHandle.isChanged()) {
+ inputWindowHandle.applyChangesToSurface(t, sc);
+ }
+ }
+
// This would reset InputWindowHandle fields to prevent it could be found by input event.
// We need to check if any new field of InputWindowHandle could impact the result.
- private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle,
- final String name, final int type, final boolean isVisible) {
- inputWindowHandle.name = name;
- inputWindowHandle.layoutParamsType = type;
- inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input
- inputWindowHandle.visible = isVisible;
- inputWindowHandle.focusable = false;
- inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
- inputWindowHandle.scaleFactor = 1;
- inputWindowHandle.layoutParamsFlags =
- FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE;
- inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
- inputWindowHandle.touchableRegion.setEmpty();
+ @VisibleForTesting
+ static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle,
+ boolean isVisible) {
+ inputWindowHandle.setDispatchingTimeoutMillis(0); // It should never receive input.
+ inputWindowHandle.setVisible(isVisible);
+ inputWindowHandle.setFocusable(false);
+ inputWindowHandle.setInputFeatures(INPUT_FEATURE_NO_INPUT_CHANNEL);
+ // The input window handle without input channel must not have a token.
+ inputWindowHandle.setToken(null);
+ inputWindowHandle.setScaleFactor(1f);
+ inputWindowHandle.setLayoutParamsFlags(
+ FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
+ inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY);
+ inputWindowHandle.clearTouchableRegion();
inputWindowHandle.setTouchableRegionCrop(null);
- inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
}
/**
@@ -635,9 +622,13 @@
*/
static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
int displayId, String name) {
- InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId);
- populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
- t.setInputWindowInfo(sc, inputWindowHandle);
+ final InputWindowHandleWrapper inputWindowHandle = new InputWindowHandleWrapper(
+ new InputWindowHandle(null /* inputApplicationHandle */, displayId));
+ inputWindowHandle.setName(name);
+ inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
+ inputWindowHandle.setTrustedOverlay(true);
+ populateOverlayInputInfo(inputWindowHandle, true /* isVisible */);
+ setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
}
static boolean isTrustedOverlay(int type) {
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
new file mode 100644
index 0000000..1fbeb1f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -0,0 +1,283 @@
+/*
+ * 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.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+
+import java.util.Objects;
+
+/**
+ * The wrapper of {@link InputWindowHandle} with field change detection to reduce unnecessary
+ * updates to surface, e.g. if there are no changes, then skip invocation of
+ * {@link SurfaceControl.Transaction#setInputWindowInfo(SurfaceControl, InputWindowHandle)}.
+ */
+class InputWindowHandleWrapper {
+ /** The wrapped handle should not be directly exposed to avoid untracked changes. */
+ private final @NonNull InputWindowHandle mHandle;
+
+ /** Whether the {@link #mHandle} is changed. */
+ private boolean mChanged = true;
+
+ InputWindowHandleWrapper(@NonNull InputWindowHandle handle) {
+ mHandle = handle;
+ }
+
+ /**
+ * Returns {@code true} if the input window handle has changed since the last invocation of
+ * {@link #applyChangesToSurface(SurfaceControl.Transaction, SurfaceControl)}}
+ */
+ boolean isChanged() {
+ return mChanged;
+ }
+
+ void forceChange() {
+ mChanged = true;
+ }
+
+ void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) {
+ t.setInputWindowInfo(sc, mHandle);
+ mChanged = false;
+ }
+
+ int getDisplayId() {
+ return mHandle.displayId;
+ }
+
+ InputApplicationHandle getInputApplicationHandle() {
+ return mHandle.inputApplicationHandle;
+ }
+
+ void setInputApplicationHandle(InputApplicationHandle handle) {
+ if (mHandle.inputApplicationHandle == handle) {
+ return;
+ }
+ mHandle.inputApplicationHandle = handle;
+ mChanged = true;
+ }
+
+ void setToken(IBinder token) {
+ if (mHandle.token == token) {
+ return;
+ }
+ mHandle.token = token;
+ mChanged = true;
+ }
+
+ void setName(String name) {
+ if (Objects.equals(mHandle.name, name)) {
+ return;
+ }
+ mHandle.name = name;
+ mChanged = true;
+ }
+
+ void setLayoutParamsFlags(int flags) {
+ if (mHandle.layoutParamsFlags == flags) {
+ return;
+ }
+ mHandle.layoutParamsFlags = flags;
+ mChanged = true;
+ }
+
+ void setLayoutParamsType(int type) {
+ if (mHandle.layoutParamsType == type) {
+ return;
+ }
+ mHandle.layoutParamsType = type;
+ mChanged = true;
+ }
+
+ void setDispatchingTimeoutMillis(long timeout) {
+ if (mHandle.dispatchingTimeoutMillis == timeout) {
+ return;
+ }
+ mHandle.dispatchingTimeoutMillis = timeout;
+ mChanged = true;
+ }
+
+ void setTouchableRegion(Region region) {
+ if (mHandle.touchableRegion.equals(region)) {
+ return;
+ }
+ mHandle.touchableRegion.set(region);
+ mChanged = true;
+ }
+
+ void clearTouchableRegion() {
+ if (mHandle.touchableRegion.isEmpty()) {
+ return;
+ }
+ mHandle.touchableRegion.setEmpty();
+ mChanged = true;
+ }
+
+ void setVisible(boolean visible) {
+ if (mHandle.visible == visible) {
+ return;
+ }
+ mHandle.visible = visible;
+ mChanged = true;
+ }
+
+ void setFocusable(boolean focusable) {
+ if (mHandle.focusable == focusable) {
+ return;
+ }
+ mHandle.focusable = focusable;
+ mChanged = true;
+ }
+
+ void setTouchOcclusionMode(int mode) {
+ if (mHandle.touchOcclusionMode == mode) {
+ return;
+ }
+ mHandle.touchOcclusionMode = mode;
+ mChanged = true;
+ }
+
+ void setHasWallpaper(boolean hasWallpaper) {
+ if (mHandle.hasWallpaper == hasWallpaper) {
+ return;
+ }
+ mHandle.hasWallpaper = hasWallpaper;
+ mChanged = true;
+ }
+
+ void setPaused(boolean paused) {
+ if (mHandle.paused == paused) {
+ return;
+ }
+ mHandle.paused = paused;
+ mChanged = true;
+ }
+
+ void setTrustedOverlay(boolean trustedOverlay) {
+ if (mHandle.trustedOverlay == trustedOverlay) {
+ return;
+ }
+ mHandle.trustedOverlay = trustedOverlay;
+ mChanged = true;
+ }
+
+ void setOwnerPid(int pid) {
+ if (mHandle.ownerPid == pid) {
+ return;
+ }
+ mHandle.ownerPid = pid;
+ mChanged = true;
+ }
+
+ void setOwnerUid(int uid) {
+ if (mHandle.ownerUid == uid) {
+ return;
+ }
+ mHandle.ownerUid = uid;
+ mChanged = true;
+ }
+
+ void setPackageName(String packageName) {
+ if (Objects.equals(mHandle.packageName, packageName)) {
+ return;
+ }
+ mHandle.packageName = packageName;
+ mChanged = true;
+ }
+
+ void setInputFeatures(int features) {
+ if (mHandle.inputFeatures == features) {
+ return;
+ }
+ mHandle.inputFeatures = features;
+ mChanged = true;
+ }
+
+ void setDisplayId(int displayId) {
+ if (mHandle.displayId == displayId) {
+ return;
+ }
+ mHandle.displayId = displayId;
+ mChanged = true;
+ }
+
+ void setPortalToDisplayId(int displayId) {
+ if (mHandle.portalToDisplayId == displayId) {
+ return;
+ }
+ mHandle.portalToDisplayId = displayId;
+ mChanged = true;
+ }
+
+ void setFrame(int left, int top, int right, int bottom) {
+ if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right
+ && mHandle.frameBottom == bottom) {
+ return;
+ }
+ mHandle.frameLeft = left;
+ mHandle.frameTop = top;
+ mHandle.frameRight = right;
+ mHandle.frameBottom = bottom;
+ mChanged = true;
+ }
+
+ void setSurfaceInset(int inset) {
+ if (mHandle.surfaceInset == inset) {
+ return;
+ }
+ mHandle.surfaceInset = inset;
+ mChanged = true;
+ }
+
+ void setScaleFactor(float scale) {
+ if (mHandle.scaleFactor == scale) {
+ return;
+ }
+ mHandle.scaleFactor = scale;
+ mChanged = true;
+ }
+
+ void replaceTouchableRegionWithCrop(@Nullable SurfaceControl bounds) {
+ setTouchableRegionCrop(bounds);
+ setReplaceTouchableRegionWithCrop(true);
+ }
+
+ void setTouchableRegionCrop(@Nullable SurfaceControl bounds) {
+ if (mHandle.touchableRegionSurfaceControl.get() == bounds) {
+ return;
+ }
+ mHandle.setTouchableRegionCrop(bounds);
+ mChanged = true;
+ }
+
+ void setReplaceTouchableRegionWithCrop(boolean replace) {
+ if (mHandle.replaceTouchableRegionWithCrop == replace) {
+ return;
+ }
+ mHandle.replaceTouchableRegionWithCrop = replace;
+ mChanged = true;
+ }
+
+ @Override
+ public String toString() {
+ return mHandle + ", changed=" + mChanged;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e7f140f..773bf54 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
@@ -35,6 +36,7 @@
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -281,6 +283,7 @@
* Called when a layout pass has occurred.
*/
void onPostLayout() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ISC.onPostLayout");
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).onPostLayout();
}
@@ -297,6 +300,7 @@
}
}
winInsetsChanged.clear();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
void onInsetsModified(InsetsControlTarget caller) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 79b88d8..de2164d 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -167,7 +167,8 @@
if (keyguardChanged) {
// Irrelevant to AOD.
- dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */);
+ dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */,
+ false /* turningScreenOn */);
setKeyguardGoingAway(false);
if (keyguardShowing) {
mDismissalRequested = false;
@@ -369,7 +370,6 @@
mService.continueWindowLayout();
}
}
- dismissMultiWindowModeForTaskIfNeeded(currentTaskControllingOcclusion);
}
/**
@@ -398,6 +398,21 @@
}
}
+ /**
+ * Called when somebody wants to turn screen on.
+ */
+ private void handleTurnScreenOn(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+
+ mStackSupervisor.wakeUp("handleTurnScreenOn");
+ if (mKeyguardShowing && canDismissKeyguard()) {
+ mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+ mDismissalRequested = true;
+ }
+ }
+
boolean isDisplayOccluded(int displayId) {
return getDisplayState(displayId).mOccluded;
}
@@ -433,14 +448,15 @@
}
private void dismissMultiWindowModeForTaskIfNeeded(
- @Nullable Task currentTaskControllingOcclusion) {
+ @Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) {
+ // If turningScreenOn is true, it means that the visibility state has changed from
+ // currentTaskControllingOcclusion and we should update windowing mode.
// TODO(b/113840485): Handle docked stack for individual display.
- if (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
+ if (!turningScreenOn && (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY))) {
return;
}
// Dismiss split screen
-
// The lock screen is currently showing, but is occluded by a window that can
// show on top of the lock screen. In this can we want to dismiss the docked
// stack since it will be complicated/risky to try to put the activity on top
@@ -579,17 +595,26 @@
&& controller.mWindowManager.isKeyguardSecure(
controller.mService.getCurrentUserId());
+ boolean occludingChange = false;
+ boolean turningScreenOn = false;
if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
&& mTopTurnScreenOnActivity != null
&& !mService.mWindowManager.mPowerManager.isInteractive()
- && (mRequestDismissKeyguard || occludedByActivity)) {
- controller.mStackSupervisor.wakeUp("handleTurnScreenOn");
+ && (mRequestDismissKeyguard || occludedByActivity
+ || controller.canDismissKeyguard())) {
+ turningScreenOn = true;
+ controller.handleTurnScreenOn(mDisplayId);
mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
}
if (lastOccluded != mOccluded) {
+ occludingChange = true;
controller.handleOccludedChanged(mDisplayId, task);
}
+
+ if (occludingChange || turningScreenOn) {
+ controller.dismissMultiWindowModeForTaskIfNeeded(task, turningScreenOn);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index b33c2f2..5b7b5a1 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -24,6 +24,8 @@
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.content.Intent.ACTION_CALL_EMERGENCY;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
@@ -33,11 +35,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -129,6 +126,18 @@
/** Tag used for disabling of keyguard */
private static final String LOCK_TASK_TAG = "Lock-to-App";
+ /** Can't be put in lockTask mode. */
+ static final int LOCK_TASK_AUTH_DONT_LOCK = 0;
+ /** Can enter app pinning with user approval. Can never start over existing lockTask task. */
+ static final int LOCK_TASK_AUTH_PINNABLE = 1;
+ /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
+ static final int LOCK_TASK_AUTH_LAUNCHABLE = 2;
+ /** Can enter lockTask without user approval. Can start over existing lockTask task. */
+ static final int LOCK_TASK_AUTH_ALLOWLISTED = 3;
+ /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
+ * lockTask task. */
+ static final int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
+
private final IBinder mToken = new LockTaskToken();
private final ActivityStackSupervisor mSupervisor;
private final Context mContext;
@@ -265,11 +274,10 @@
}
/**
- * @return whether the requested task is allowed to be locked (either allowlisted, or declares
- * lockTaskMode="always" in the manifest).
+ * @return whether the requested task auth is allowed to be locked.
*/
- boolean isTaskAllowlisted(Task task) {
- switch(task.mLockTaskAuth) {
+ static boolean isTaskAuthAllowlisted(int lockTaskAuth) {
+ switch(lockTaskAuth) {
case LOCK_TASK_AUTH_ALLOWLISTED:
case LOCK_TASK_AUTH_LAUNCHABLE:
case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
@@ -293,7 +301,30 @@
* @return whether the requested task is disallowed to be launched.
*/
boolean isLockTaskModeViolation(Task task, boolean isNewClearTask) {
- if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
+ // TODO: Double check what's going on here. If the task is already in lock task mode, it's
+ // likely allowlisted, so will return false below.
+ if (isTaskLocked(task) && !isNewClearTask) {
+ // If the task is already at the top and won't be cleared, then allow the operation
+ } else if (isLockTaskModeViolationInternal(task, task.mUserId, task.intent,
+ task.mLockTaskAuth)) {
+ showLockTaskToast();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param activity an activity that is going to be started in a new task as the root activity.
+ * @return whether the given activity is allowed to be launched.
+ */
+ boolean isNewTaskLockTaskModeViolation(ActivityRecord activity) {
+ // Use the belong task (if any) to perform the lock task checks
+ if (activity.getTask() != null) {
+ return isLockTaskModeViolation(activity.getTask());
+ }
+
+ int auth = getLockTaskAuth(activity, null /* task */);
+ if (isLockTaskModeViolationInternal(activity, activity.mUserId, activity.intent, auth)) {
showLockTaskToast();
return true;
}
@@ -310,25 +341,19 @@
return mLockTaskModeTasks.get(0);
}
- private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) {
- // TODO: Double check what's going on here. If the task is already in lock task mode, it's
- // likely allowlisted, so will return false below.
- if (isTaskLocked(task) && !isNewClearTask) {
- // If the task is already at the top and won't be cleared, then allow the operation
- return false;
- }
-
+ private boolean isLockTaskModeViolationInternal(WindowContainer wc, int userId,
+ Intent intent, int taskAuth) {
// Allow recents activity if enabled by policy
- if (task.isActivityTypeRecents() && isRecentsAllowed(task.mUserId)) {
+ if (wc.isActivityTypeRecents() && isRecentsAllowed(userId)) {
return false;
}
// Allow emergency calling when the device is protected by a locked keyguard
- if (isKeyguardAllowed(task.mUserId) && isEmergencyCallTask(task)) {
+ if (isKeyguardAllowed(userId) && isEmergencyCallIntent(intent)) {
return false;
}
- return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty());
+ return !(isTaskAuthAllowlisted(taskAuth) || mLockTaskModeTasks.isEmpty());
}
private boolean isRecentsAllowed(int userId) {
@@ -360,8 +385,7 @@
return isPackageAllowlisted(userId, packageName);
}
- private boolean isEmergencyCallTask(Task task) {
- final Intent intent = task.intent;
+ private boolean isEmergencyCallIntent(Intent intent) {
if (intent == null) {
return false;
}
@@ -697,6 +721,40 @@
}
}
+ int getLockTaskAuth(@Nullable ActivityRecord rootActivity, @Nullable Task task) {
+ if (rootActivity == null && task == null) {
+ return LOCK_TASK_AUTH_DONT_LOCK;
+ }
+ if (rootActivity == null) {
+ return LOCK_TASK_AUTH_PINNABLE;
+ }
+
+ final String pkg = (task == null || task.realActivity == null) ? null
+ : task.realActivity.getPackageName();
+ final int userId = task != null ? task.mUserId : rootActivity.mUserId;
+ int lockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
+ switch (rootActivity.lockTaskLaunchMode) {
+ case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+ lockTaskAuth = isPackageAllowlisted(userId, pkg)
+ ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_NEVER:
+ lockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+ lockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
+ lockTaskAuth = isPackageAllowlisted(userId, pkg)
+ ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+ break;
+ }
+ return lockTaskAuth;
+ }
+
boolean isPackageAllowlisted(int userId, String pkg) {
if (pkg == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 37f9082..45cd359 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -658,8 +658,8 @@
}
for (int i = mTasks.size() - 1; i >= 0; --i) {
final Task task = mTasks.get(i);
- if (task.mUserId == userId
- && !mService.getLockTaskController().isTaskAllowlisted(task)) {
+ if (task.mUserId == userId && !mService.getLockTaskController().isTaskAuthAllowlisted(
+ task.mLockTaskAuth)) {
remove(task);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 757c57f..2749cc9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -59,7 +59,7 @@
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
@@ -76,9 +76,9 @@
import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
-import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
+import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
+import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -232,20 +232,19 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- MATCH_TASK_IN_STACKS_ONLY,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ MATCH_ATTACHED_TASK_ONLY,
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS,
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE
})
public @interface AnyTaskForIdMatchTaskMode {
}
- // Match only tasks in the current stacks
- static final int MATCH_TASK_IN_STACKS_ONLY = 0;
- // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
- // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
- // provided stack id
- static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+ // Match only tasks that are attached to the hierarchy
+ static final int MATCH_ATTACHED_TASK_ONLY = 0;
+ // Match either attached tasks, or in the recent tasks if the tasks are detached
+ static final int MATCH_ATTACHED_TASK_OR_RECENT_TASKS = 1;
+ // Match either attached tasks, or in the recent tasks, restoring it to the provided task id
+ static final int MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE = 2;
ActivityTaskManagerService mService;
ActivityStackSupervisor mStackSupervisor;
@@ -1953,7 +1952,7 @@
for (int taskNdx = displayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) {
final Task rootTask = displayArea.getStackAt(taskNdx);
- if (rootTask.getVisibility(null /*starting*/) == STACK_VISIBILITY_INVISIBLE) {
+ if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
break;
}
@@ -2556,7 +2555,7 @@
@Override
public void onDisplayAdded(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+ if (DEBUG_ROOT_TASK) Slog.v(TAG, "Display added displayId=" + displayId);
synchronized (mService.mGlobalLock) {
final DisplayContent display = getDisplayContentOrCreate(displayId);
if (display == null) {
@@ -2578,7 +2577,7 @@
@Override
public void onDisplayRemoved(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+ if (DEBUG_ROOT_TASK) Slog.v(TAG, "Display removed displayId=" + displayId);
if (displayId == DEFAULT_DISPLAY) {
throw new IllegalArgumentException("Can't remove the primary display.");
}
@@ -2595,7 +2594,7 @@
@Override
public void onDisplayChanged(int displayId) {
- if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+ if (DEBUG_ROOT_TASK) Slog.v(TAG, "Display changed displayId=" + displayId);
synchronized (mService.mGlobalLock) {
final DisplayContent displayContent = getDisplayContent(displayId);
if (displayContent != null) {
@@ -2888,7 +2887,7 @@
// Temporarily set the task id to invalid in case in re-entry.
options.setLaunchTaskId(INVALID_TASK_ID);
final Task task = anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
options.setLaunchTaskId(taskId);
if (task != null) {
return task.getRootTask();
@@ -3444,7 +3443,7 @@
}
Task anyTaskForId(int id) {
- return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+ return anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE);
}
Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode) {
@@ -3462,7 +3461,7 @@
Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
@Nullable ActivityOptions aOptions, boolean onTop) {
// If options are set, ensure that we are attempting to actually restore a task
- if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+ if (matchMode != MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
throw new IllegalArgumentException("Should not specify activity options for non-restore"
+ " lookup");
}
@@ -3480,7 +3479,7 @@
getLaunchStack(null, aOptions, task, onTop);
if (launchStack != null && task.getRootTask() != launchStack) {
final int reparentMode = onTop
- ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+ ? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
"anyTaskForId");
}
@@ -3489,7 +3488,7 @@
}
// If we are matching stack tasks only, return now
- if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+ if (matchMode == MATCH_ATTACHED_TASK_ONLY) {
return null;
}
@@ -3506,11 +3505,11 @@
return null;
}
- if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+ if (matchMode == MATCH_ATTACHED_TASK_OR_RECENT_TASKS) {
return task;
}
- // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+ // Implicitly, this case is MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE
if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) {
if (DEBUG_RECENTS) {
Slog.w(TAG_RECENTS,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6fbd351..b46e796 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -24,6 +24,7 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_SHORTCUT_ID;
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -43,6 +44,7 @@
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -288,7 +290,8 @@
public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
// Validate and resolve ClipDescription data before clearing the calling identity
- validateAndResolveDragMimeTypeExtras(data, Binder.getCallingUid());
+ validateAndResolveDragMimeTypeExtras(data, Binder.getCallingUid(), Binder.getCallingPid(),
+ mPackageName);
final long ident = Binder.clearCallingIdentity();
try {
return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
@@ -302,8 +305,9 @@
* Validates the given drag data.
*/
@VisibleForTesting
- public void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid) {
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid, int callingPid,
+ String callingPackage) {
+ if (callingUid == Process.SYSTEM_UID) {
throw new IllegalStateException("Need to validate before calling identify is cleared");
}
final ClipDescription desc = data != null ? data.getDescription() : null;
@@ -358,26 +362,58 @@
Binder.restoreCallingIdentity(origId);
}
} else if (hasShortcut) {
+ // Restrict who can start a shortcut drag since it will start the shortcut as the
+ // target shortcut package
mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
"performDrag");
for (int i = 0; i < data.getItemCount(); i++) {
- final Intent intent = data.getItemAt(i).getIntent();
+ final ClipData.Item item = data.getItemAt(i);
+ final Intent intent = item.getIntent();
+ final String shortcutId = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (!intent.hasExtra(EXTRA_SHORTCUT_ID)
- || TextUtils.isEmpty(intent.getStringExtra(EXTRA_SHORTCUT_ID))
+ if (TextUtils.isEmpty(shortcutId)
+ || TextUtils.isEmpty(packageName)
|| user == null) {
- throw new IllegalArgumentException("Clip item must include the shortcut id and "
- + "the user to launch for.");
+ throw new IllegalArgumentException("Clip item must include the package name, "
+ + "shortcut id, and the user to launch for.");
}
+ final ShortcutServiceInternal shortcutService =
+ LocalServices.getService(ShortcutServiceInternal.class);
+ final Intent[] shortcutIntents = shortcutService.createShortcutIntents(
+ callingUid, callingPackage, packageName, shortcutId,
+ user.getIdentifier(), callingPid, callingUid);
+ if (shortcutIntents == null || shortcutIntents.length == 0) {
+ throw new IllegalArgumentException("Invalid shortcut id");
+ }
+ final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
+ shortcutIntents[0], null /* resolvedType */, user.getIdentifier(),
+ callingUid);
+ item.setActivityInfo(info);
}
} else if (hasTask) {
+ // TODO(b/169894807): Consider opening this up for tasks from the same app as the caller
mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
"performDrag");
for (int i = 0; i < data.getItemCount(); i++) {
- final Intent intent = data.getItemAt(i).getIntent();
- if (intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID) == INVALID_TASK_ID) {
+ final ClipData.Item item = data.getItemAt(i);
+ final Intent intent = item.getIntent();
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ if (taskId == INVALID_TASK_ID) {
throw new IllegalArgumentException("Clip item must include the task id.");
}
+ final Task task = mService.mRoot.anyTaskForId(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("Invalid task id.");
+ }
+ if (task.getRootActivity() != null) {
+ item.setActivityInfo(task.getRootActivity().info);
+ } else {
+ // Resolve the activity info manually if the task was restored after reboot
+ final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
+ task.intent, null /* resolvedType */, task.mUserId, callingUid);
+ item.setActivityInfo(info);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9273bf7..cbb3c42 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -43,10 +43,6 @@
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -107,7 +103,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
@@ -120,6 +116,11 @@
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.Task.ActivityState.PAUSING;
@@ -145,7 +146,7 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
@@ -251,7 +252,7 @@
static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
+ private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
@@ -308,38 +309,37 @@
private float mShadowRadius = 0;
/**
- * The modes to control how the stack is moved to the front when calling {@link Task#reparent}.
+ * The modes to control how root task is moved to the front when calling {@link Task#reparent}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- REPARENT_MOVE_STACK_TO_FRONT,
- REPARENT_KEEP_STACK_AT_FRONT,
- REPARENT_LEAVE_STACK_IN_PLACE
+ REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+ REPARENT_LEAVE_ROOT_TASK_IN_PLACE
})
- @interface ReparentMoveStackMode {}
- // Moves the stack to the front if it was not at the front
- static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
- // Only moves the stack to the front if it was focused or front most already
- static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
- // Do not move the stack as a part of reparenting
- static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ @interface ReparentMoveRootTaskMode {}
+ // Moves the root task to the front if it was not at the front
+ static final int REPARENT_MOVE_ROOT_TASK_TO_FRONT = 0;
+ // Only moves the root task to the front if it was focused or front most already
+ static final int REPARENT_KEEP_ROOT_TASK_AT_FRONT = 1;
+ // Do not move the root task as a part of reparenting
+ static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2;
- // TODO (b/157876447): switch to Task related name
- @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
- STACK_VISIBILITY_VISIBLE,
- STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- STACK_VISIBILITY_INVISIBLE,
+ @IntDef(prefix = {"TASK_VISIBILITY"}, value = {
+ TASK_VISIBILITY_VISIBLE,
+ TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ TASK_VISIBILITY_INVISIBLE,
})
- @interface StackVisibility {}
+ @interface TaskVisibility {}
- /** Stack is visible. No other stacks on top that fully or partially occlude it. */
- static final int STACK_VISIBILITY_VISIBLE = 0;
+ /** Task is visible. No other tasks on top that fully or partially occlude it. */
+ static final int TASK_VISIBILITY_VISIBLE = 0;
- /** Stack is partially occluded by other translucent stack(s) on top of it. */
- static final int STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+ /** Task is partially occluded by other translucent task(s) on top of it. */
+ static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
- /** Stack is completely invisible. */
- static final int STACK_VISIBILITY_INVISIBLE = 2;
+ /** Task is completely invisible. */
+ static final int TASK_VISIBILITY_INVISIBLE = 2;
enum ActivityState {
INITIALIZING,
@@ -407,17 +407,6 @@
boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
// was changed.
- /** Can't be put in lockTask mode. */
- final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
- /** Can enter app pinning with user approval. Can never start over existing lockTask task. */
- final static int LOCK_TASK_AUTH_PINNABLE = 1;
- /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
- final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
- /** Can enter lockTask without user approval. Can start over existing lockTask task. */
- final static int LOCK_TASK_AUTH_ALLOWLISTED = 3;
- /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing
- * lockTask task. */
- final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
int mLockTaskUid = -1; // The uid of the application that called startLockTask().
@@ -964,7 +953,7 @@
mAtmService.getLockTaskController().clearLockedTask(this);
}
if (shouldDeferRemoval()) {
- if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
+ if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
return;
}
removeImmediately();
@@ -1056,7 +1045,7 @@
/** Convenience method to reparent a task to the top or bottom position of the stack. */
boolean reparent(Task preferredStack, boolean toTop,
- @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ @ReparentMoveRootTaskMode int moveStackMode, boolean animate, boolean deferResume,
String reason) {
return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume,
true /* schedulePictureInPictureModeChange */, reason);
@@ -1067,7 +1056,7 @@
* an option to skip scheduling the picture-in-picture mode change.
*/
boolean reparent(Task preferredStack, boolean toTop,
- @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ @ReparentMoveRootTaskMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate,
deferResume, schedulePictureInPictureModeChange, reason);
@@ -1075,7 +1064,7 @@
/** Convenience method to reparent a task to a specific position of the stack. */
boolean reparent(Task preferredStack, int position,
- @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ @ReparentMoveRootTaskMode int moveStackMode, boolean animate, boolean deferResume,
String reason) {
return reparent(preferredStack, position, moveStackMode, animate, deferResume,
true /* schedulePictureInPictureModeChange */, reason);
@@ -1101,7 +1090,7 @@
// TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
// re-parenting the task. Can only be done when we are no longer using static stack Ids.
boolean reparent(Task preferredStack, int position,
- @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
+ @ReparentMoveRootTaskMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
final ActivityStackSupervisor supervisor = mStackSupervisor;
final RootWindowContainer root = mRootWindowContainer;
@@ -1156,8 +1145,9 @@
final boolean wasFront = r != null && sourceStack.isTopStackInDisplayArea()
&& (sourceStack.topRunningActivity() == r);
- final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
- || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
+ final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_ROOT_TASK_TO_FRONT
+ || (moveStackMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT
+ && (wasFocused || wasFront));
reparent(toStack, position, moveStackToFront, reason);
@@ -1181,7 +1171,7 @@
toStack.prepareFreezingTaskBounds();
if (toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
+ && moveStackMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) {
// Move recents to front so it is not behind home stack when going into docked
// mode
mStackSupervisor.moveRecentsStackToFront(reason);
@@ -1532,7 +1522,7 @@
return;
}
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) Slog.d(TAG_STACK,
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
"setResumedActivity stack:" + this + " + from: "
+ mResumedActivity + " to:" + r + " reason:" + reason);
mResumedActivity = r;
@@ -1941,32 +1931,7 @@
}
private void setLockTaskAuth(@Nullable ActivityRecord r) {
- if (r == null) {
- mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
- return;
- }
-
- final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
- final LockTaskController lockTaskController = mAtmService.getLockTaskController();
- switch (r.lockTaskLaunchMode) {
- case LOCK_TASK_LAUNCH_MODE_DEFAULT:
- mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
- ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE;
- break;
-
- case LOCK_TASK_LAUNCH_MODE_NEVER:
- mLockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
- break;
-
- case LOCK_TASK_LAUNCH_MODE_ALWAYS:
- mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
- break;
-
- case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
- mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg)
- ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
- break;
- }
+ mLockTaskAuth = mAtmService.getLockTaskController().getLockTaskAuth(r, this);
ProtoLog.d(WM_DEBUG_LOCKTASK, "setLockTaskAuth: task=%s mLockTaskAuth=%s", this,
lockTaskAuthToString());
}
@@ -2210,8 +2175,8 @@
}
if (state == RESUMED) {
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
- Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:" + reason);
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason);
}
setResumedActivity(record, reason + " - onActivityStateChanged");
if (record == mRootWindowContainer.getTopResumedActivity()) {
@@ -3265,7 +3230,7 @@
@Override
void removeImmediately() {
- if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
+ if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
// If applicable let the TaskOrganizer know the Task is vanishing.
@@ -3276,7 +3241,7 @@
// TODO: Consolidate this with Task.reparent()
void reparent(Task stack, int position, boolean moveParents, String reason) {
- if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
+ if (DEBUG_ROOT_TASK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
+ " from stack=" + getRootTask());
EventLogTags.writeWmTaskRemoved(mTaskId, "reParentTask:" + reason);
@@ -4180,7 +4145,7 @@
* @param starting The currently starting activity or null if there is none.
*/
boolean shouldBeVisible(ActivityRecord starting) {
- return getVisibility(starting) != STACK_VISIBILITY_INVISIBLE;
+ return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE;
}
/**
@@ -4188,14 +4153,14 @@
*
* @param starting The currently starting activity or null if there is none.
*/
- @Task.StackVisibility
+ @TaskVisibility
int getVisibility(ActivityRecord starting) {
if (!isAttached() || isForceHidden()) {
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
if (isTopActivityLaunchedBehind()) {
- return STACK_VISIBILITY_VISIBLE;
+ return TASK_VISIBILITY_VISIBLE;
}
boolean gotSplitScreenStack = false;
@@ -4211,10 +4176,10 @@
final WindowContainer parent = getParent();
if (parent.asTask() != null) {
final int parentVisibility = parent.asTask().getVisibility(starting);
- if (parentVisibility == STACK_VISIBILITY_INVISIBLE) {
+ if (parentVisibility == TASK_VISIBILITY_INVISIBLE) {
// Can't be visible if parent isn't visible
- return STACK_VISIBILITY_INVISIBLE;
- } else if (parentVisibility == STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
+ return TASK_VISIBILITY_INVISIBLE;
+ } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
// Parent is behind a translucent container so the highest visibility this container
// can get is that.
gotTranslucentFullscreen = true;
@@ -4249,7 +4214,7 @@
gotTranslucentFullscreen = true;
continue;
}
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
} else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& !gotOpaqueSplitScreenPrimary) {
gotSplitScreenStack = true;
@@ -4258,7 +4223,7 @@
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& gotOpaqueSplitScreenPrimary) {
// Can not be visible behind another opaque stack in split-screen-primary mode.
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
} else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
&& !gotOpaqueSplitScreenSecondary) {
@@ -4268,24 +4233,24 @@
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
&& gotOpaqueSplitScreenSecondary) {
// Can not be visible behind another opaque stack in split-screen-secondary mode.
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
}
if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
// Can not be visible if we are in split-screen windowing mode and both halves of
// the screen are opaque.
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
if (isAssistantType && gotSplitScreenStack) {
// Assistant stack can't be visible behind split-screen. In addition to this not
// making sense, it also works around an issue here we boost the z-order of the
// assistant window surfaces in window manager whenever it is visible.
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
}
if (!shouldBeVisible) {
- return STACK_VISIBILITY_INVISIBLE;
+ return TASK_VISIBILITY_INVISIBLE;
}
// Handle cases when there can be a translucent split-screen stack on top.
@@ -4293,26 +4258,26 @@
case WINDOWING_MODE_FULLSCREEN:
if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
// At least one of the split-screen stacks that covers this one is translucent.
- return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
if (gotTranslucentSplitScreenPrimary) {
// Covered by translucent primary split-screen on top.
- return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
if (gotTranslucentSplitScreenSecondary) {
// Covered by translucent secondary split-screen on top.
- return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
}
// Lastly - check if there is a translucent fullscreen stack on top.
- return gotTranslucentFullscreen ? STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
- : STACK_VISIBILITY_VISIBLE;
+ return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
+ : TASK_VISIBILITY_VISIBLE;
}
private boolean isTopActivityLaunchedBehind() {
@@ -7326,7 +7291,7 @@
boolean toTop = position >= getChildCount();
boolean includingParents = toTop || getDisplayArea().getNextFocusableStack(this,
true /* ignoreCurrent */) == null;
- if (WindowManagerDebugConfig.DEBUG_STACK) {
+ if (WindowManagerDebugConfig.DEBUG_ROOT_TASK) {
Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
}
positionChildAt(position, task, includingParents);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index e721319..bda5759 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -37,11 +37,11 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
+import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateStack;
import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
@@ -311,7 +311,7 @@
@Override
void addChild(Task task, int position) {
- if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
+ if (DEBUG_ROOT_TASK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
addStackReferenceIfNeeded(task);
position = findPositionForStack(position, task, true /* adding */);
@@ -831,8 +831,8 @@
}
void onStackRemoved(Task stack) {
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
- Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId="
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.v(TAG_ROOT_TASK, "removeStack: detaching " + stack + " from displayId="
+ mDisplayContent.mDisplayId);
}
if (mPreferredTopFocusableStack == stack) {
@@ -870,7 +870,7 @@
homeParentTask.positionChildAtBottom(task);
} else {
task.reparent(homeParentTask, false /* toTop */,
- Task.REPARENT_LEAVE_STACK_IN_PLACE, false /* animate */,
+ Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE, false /* animate */,
false /* deferResume */, "positionTaskBehindHome");
}
}
@@ -1205,8 +1205,8 @@
}
}
final Task currentFocusedStack = getFocusedStack();
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
- Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.d(TAG_ROOT_TASK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ mLastFocusedStack + " to=" + currentFocusedStack);
}
mLastFocusedStack = currentFocusedStack;
@@ -1230,7 +1230,7 @@
final Task stack = getStackAt(stackNdx);
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null
- && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
+ && (stack.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
|| !stack.isTopActivityFocusable())) {
ProtoLog.d(WM_DEBUG_STATES, "pauseBackStacks: stack=%s "
+ "mResumedActivity=%s", stack, resumedActivity);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index a1c5670..bd52b66 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -493,8 +493,7 @@
mTmpTaskInfo.positionInParent,
lastInfo.positionInParent)
|| mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
- || mTmpTaskInfo.getConfiguration().windowConfiguration.getWindowingMode()
- != lastInfo.getConfiguration().windowConfiguration.getWindowingMode()
+ || mTmpTaskInfo.getWindowingMode() != lastInfo.getWindowingMode()
|| !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
if (!changed) {
int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index a3dc290..eff4b9e 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import android.annotation.NonNull;
import android.graphics.Bitmap;
@@ -330,7 +330,7 @@
final int taskId = task.mTaskId;
if (mService.mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
} else if (userId != task.mUserId) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index a6f0f46..614d221 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -225,10 +225,8 @@
mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer());
- mDragApplicationHandle = new InputApplicationHandle(new Binder());
- mDragApplicationHandle.name = TAG;
- mDragApplicationHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-
+ mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
+ DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
displayContent.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 93b0fd9..74337c2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -50,7 +50,7 @@
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
static final boolean DEBUG_TASK_POSITIONING = false;
- static final boolean DEBUG_STACK = false;
+ static final boolean DEBUG_ROOT_TASK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
static final boolean SHOW_VERBOSE_TRANSACTIONS = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 33b3e59..e7d9e6b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7697,6 +7697,7 @@
if (imeTarget == null) {
return;
}
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
// If InsetsControlTarget doesn't have a window, its using remoteControlTarget which
@@ -7710,6 +7711,7 @@
@Override
public void hideIme(IBinder imeTargetWindowToken, int displayId) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.hideIme");
synchronized (mGlobalLock) {
WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
ProtoLog.d(WM_DEBUG_IME, "hideIme target: %s ", imeTarget);
@@ -7730,6 +7732,7 @@
WindowInsets.Type.ime(), true /* fromIme */);
}
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@Override
@@ -8392,7 +8395,7 @@
embeddedWindow.getName());
return;
}
- t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken,
+ t.requestFocusTransfer(newFocusTarget.mInputChannelToken, targetInputToken,
displayId).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + newFocusTarget,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9234390..fc06461 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -556,9 +556,17 @@
boolean mWindowRemovalAllowed;
// Input channel and input window handle used by the input dispatcher.
- final InputWindowHandle mInputWindowHandle;
+ final InputWindowHandleWrapper mInputWindowHandle;
InputChannel mInputChannel;
+ /**
+ * The token will be assigned to {@link InputWindowHandle#token} if this window can receive
+ * input event. Note that the token of associated input window handle can be cleared if this
+ * window becomes unable to receive input, but this field will remain until the input channel
+ * is actually disposed.
+ */
+ IBinder mInputChannelToken;
+
// Used to improve performance of toString()
private String mStringNameCache;
private CharSequence mLastTitle;
@@ -856,6 +864,21 @@
DeathRecipient deathRecipient = new DeathRecipient();
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
+ mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
+ mActivityRecord != null
+ ? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
+ getDisplayId()));
+ mInputWindowHandle.setOwnerPid(s.mPid);
+ mInputWindowHandle.setOwnerUid(s.mUid);
+ mInputWindowHandle.setName(getName());
+ mInputWindowHandle.setPackageName(mAttrs.packageName);
+ mInputWindowHandle.setLayoutParamsType(mAttrs.type);
+ // Check private trusted overlay flag and window type to set trustedOverlay variable of
+ // input window handle.
+ mInputWindowHandle.setTrustedOverlay(
+ ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
+ && mOwnerCanAddInternalSystemWindow)
+ || InputMonitor.isTrustedOverlay(mAttrs.type));
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -871,7 +894,6 @@
mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
- mInputWindowHandle = null;
mWinAnimator = null;
mWpcForDisplayConfigChanges = null;
return;
@@ -919,16 +941,6 @@
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
- mInputWindowHandle = new InputWindowHandle(
- mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
- getDisplayId());
-
- // Check private trusted overlay flag and window type to set trustedOverlay variable of
- // input window handle.
- mInputWindowHandle.trustedOverlay =
- (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
- && mOwnerCanAddInternalSystemWindow;
- mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1496,9 +1508,9 @@
}
super.onDisplayChanged(dc);
// Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
- if (dc != null && mInputWindowHandle.displayId != dc.getDisplayId()) {
+ if (dc != null && mInputWindowHandle.getDisplayId() != dc.getDisplayId()) {
mLayoutSeq = dc.mLayoutSeq - 1;
- mInputWindowHandle.displayId = dc.getDisplayId();
+ mInputWindowHandle.setDisplayId(dc.getDisplayId());
}
}
@@ -2470,7 +2482,9 @@
}
String name = getName();
mInputChannel = mWmService.mInputManager.createInputChannel(name);
- mInputWindowHandle.token = mInputChannel.getToken();
+ mInputChannelToken = mInputChannel.getToken();
+ mInputWindowHandle.setToken(mInputChannelToken);
+ mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) {
mInputChannel.copyTo(outInputChannel);
} else {
@@ -2479,7 +2493,6 @@
// Create fake event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
}
- mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
void disposeInputChannel() {
@@ -2487,17 +2500,19 @@
mDeadWindowEventReceiver.dispose();
mDeadWindowEventReceiver = null;
}
+ if (mInputChannelToken != null) {
+ // Unregister server channel first otherwise it complains about broken channel.
+ mWmService.mInputManager.removeInputChannel(mInputChannelToken);
+ mWmService.mKeyInterceptionInfoForToken.remove(mInputChannelToken);
+ mWmService.mInputToWindowMap.remove(mInputChannelToken);
+ mInputChannelToken = null;
+ }
- // unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
- mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
-
mInputChannel.dispose();
mInputChannel = null;
}
- mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
- mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
- mInputWindowHandle.token = null;
+ mInputWindowHandle.setToken(null);
}
/** Returns true if the replacement window was removed. */
@@ -2567,11 +2582,8 @@
}
}
- int getSurfaceTouchableRegion(InputWindowHandle inputWindowHandle, int flags) {
+ int getSurfaceTouchableRegion(Region region, int flags) {
final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
- final Region region = inputWindowHandle.touchableRegion;
- setTouchableRegionCropIfNeeded(inputWindowHandle);
-
if (modal) {
flags |= FLAG_NOT_TOUCH_MODAL;
if (mActivityRecord != null) {
@@ -2593,7 +2605,10 @@
}
// Translate to surface based coordinates.
- region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
+ final Rect frame = mWindowFrames.mFrame;
+ if (frame.left != 0 || frame.top != 0) {
+ region.translate(-frame.left, -frame.top);
+ }
// TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
// scaling but the existing logic doesn't expect that. The result is that the already-
@@ -3442,7 +3457,9 @@
break;
case TOUCHABLE_INSETS_REGION: {
outRegion.set(mGivenTouchableRegion);
- outRegion.translate(frame.left, frame.top);
+ if (frame.left != 0 || frame.top != 0) {
+ outRegion.translate(frame.left, frame.top);
+ }
break;
}
}
@@ -3469,22 +3486,6 @@
}
}
- private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) {
- final Task task = getTask();
- if (task == null || !task.cropWindowsToStackBounds()) {
- handle.setTouchableRegionCrop(null);
- return;
- }
-
- final Task stack = task.getRootTask();
- if (stack == null || inFreeformWindowingMode()) {
- handle.setTouchableRegionCrop(null);
- return;
- }
-
- handle.setTouchableRegionCrop(stack.getSurfaceControl());
- }
-
private void cropRegionToStackBoundsIfNeeded(Region region) {
final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d845b12..72aa766 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -488,6 +488,9 @@
mSurfaceFormat = format;
w.setHasSurface(true);
+ // The surface instance is changed. Make sure the input info can be applied to the
+ // new surface, e.g. relaunch activity.
+ w.mInputWindowHandle.forceChange();
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c1d5f19..3a00196 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -650,7 +650,7 @@
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
KeyCharacterMap::loadContents(filenameChars.c_str(), contentsChars.c_str(),
- KeyCharacterMap::FORMAT_OVERLAY);
+ KeyCharacterMap::Format::OVERLAY);
if (ret) {
result = *ret;
}
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
index 0801c88..ca02f70 100644
--- a/services/core/xsd/cec-config/cec-config.xsd
+++ b/services/core/xsd/cec-config/cec-config.xsd
@@ -12,6 +12,7 @@
</xs:element>
<xs:complexType name="setting">
<xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="value-type" type="xs:string"/>
<xs:attribute name="user-configurable" type="xs:boolean"/>
<xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
<xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
@@ -23,5 +24,6 @@
</xs:complexType>
<xs:complexType name="value">
<xs:attribute name="string-value" type="xs:string"/>
+ <xs:attribute name="int-value" type="xs:int"/>
</xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
index 34faf45..00dd15b 100644
--- a/services/core/xsd/cec-config/schema/current.txt
+++ b/services/core/xsd/cec-config/schema/current.txt
@@ -12,15 +12,19 @@
method public com.android.server.hdmi.cec.config.Value getDefaultValue();
method public String getName();
method public boolean getUserConfigurable();
+ method public String getValueType();
method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
method public void setName(String);
method public void setUserConfigurable(boolean);
+ method public void setValueType(String);
}
public class Value {
ctor public Value();
+ method public int getIntValue();
method public String getStringValue();
+ method public void setIntValue(int);
method public void setStringValue(String);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 736a7be..2c92ae4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -27,9 +27,11 @@
import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import android.content.ContentResolver;
@@ -79,8 +81,11 @@
private static final String CALLING_PACKAGE2 = "com.package.name2";
private static final String NAMESPACE1 = "namespace1";
private static final String NAMESPACE2 = "namespace2";
+ private static final String NAMESPACE3 = "namespace3";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
+ private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
+ "persist.device_config.configuration.disable_rescue_party_factory_reset";
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
@@ -183,27 +188,38 @@
@Test
public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
+ RescueParty.onSettingsProviderPublished(mMockContext);
+ verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+ mMonitorCallbackCaptor.capture()));
+
noteBoot();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
+ // Record DeviceConfig accesses
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+ RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
+
+ final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
+
noteBoot();
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
- verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -230,7 +246,6 @@
notePersistentAppCrash();
- verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -247,6 +262,7 @@
monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
// Fake DeviceConfig value changes
monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
verify(mMockPackageWatchdog).startObservingHealth(observer,
@@ -255,10 +271,15 @@
verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
mPackageListCaptor.capture(),
eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
+ monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
+ verify(mMockPackageWatchdog).startObservingHealth(observer,
+ Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
assertTrue(mPackageListCaptor.getValue().containsAll(
Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2)));
// Perform and verify scoped resets
final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
+ final String[] expectedAllResetNamespaces =
+ new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces);
@@ -273,13 +294,12 @@
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/null);
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
- verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@@ -288,7 +308,6 @@
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot();
}
- verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@@ -337,12 +356,25 @@
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
- // Restore the property value initalized in SetUp()
+ // Restore the property value initialized in SetUp()
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@Test
+ public void testDisablingFactoryResetByDeviceConfigFlag() {
+ SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true));
+
+ for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+ noteBoot();
+ }
+ assertFalse(RescueParty.isAttemptingFactoryReset());
+
+ // Restore the property value initialized in SetUp()
+ SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");
+ }
+
+ @Test
public void testHealthCheckLevels() {
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
@@ -437,7 +469,7 @@
eq(resetMode), anyInt()));
// Verify DeviceConfig resets
if (resetNamespaces == null) {
- verify(() -> DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null));
+ verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never());
} else {
for (String namespace : resetNamespaces) {
verify(() -> DeviceConfig.resetToDefaults(resetMode, namespace));
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index db4aba5..8e4942e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -47,6 +47,7 @@
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
@@ -62,6 +63,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -365,7 +367,7 @@
}
private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) {
- setTestAlarm(type, triggerTime, pi, 0, FLAG_IDLE_UNTIL, TEST_CALLING_UID);
+ setTestAlarm(type, triggerTime, pi, 0, FLAG_IDLE_UNTIL | FLAG_STANDALONE, TEST_CALLING_UID);
}
private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) {
@@ -410,6 +412,12 @@
mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
}
+ private void setDeviceConfigBoolean(String key, boolean val) {
+ mDeviceConfigKeys.add(key);
+ doReturn(val).when(mDeviceConfigProperties).getBoolean(eq(key), anyBoolean());
+ mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
+ }
+
/**
* Lowers quotas to make testing feasible. Careful while calling as this will replace any
* existing settings for the calling test.
@@ -1382,6 +1390,35 @@
}
}
+ @Test
+ public void alarmStoreMigration() {
+ setDeviceConfigBoolean(KEY_LAZY_BATCHING, false);
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 1, pis[i]);
+ }
+
+ final ArrayList<Alarm> alarmsBefore = mService.mAlarmStore.asList();
+ assertEquals(numAlarms, alarmsBefore.size());
+ for (int i = 0; i < numAlarms; i++) {
+ final PendingIntent pi = pis[i];
+ assertTrue(i + "th PendingIntent missing: ",
+ alarmsBefore.removeIf(a -> a.matches(pi, null)));
+ }
+
+ setDeviceConfigBoolean(KEY_LAZY_BATCHING, true);
+
+ final ArrayList<Alarm> alarmsAfter = mService.mAlarmStore.asList();
+ assertEquals(numAlarms, alarmsAfter.size());
+ for (int i = 0; i < numAlarms; i++) {
+ final PendingIntent pi = pis[i];
+ assertTrue(i + "th PendingIntent missing: ",
+ alarmsAfter.removeIf(a -> a.matches(pi, null)));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index c4fc61a..42fa3d4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -16,6 +16,9 @@
package com.android.server.alarm;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
@@ -23,35 +26,50 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.platform.test.annotations.Presubmit;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
import java.util.ArrayList;
@Presubmit
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class AlarmStoreTest {
- private AlarmStore mAlarmStore;
- @Before
- public void setUp() {
- mAlarmStore = new BatchingAlarmStore(null);
+ @Parameter
+ public AlarmStore mAlarmStore;
+
+ @Parameters
+ public static Object[] stores() {
+ return new AlarmStore[]{
+ new LazyAlarmStore(),
+ new BatchingAlarmStore(),
+ };
}
private static Alarm createAlarm(long whenElapsed, long windowLength) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, 0);
+ return createAlarm(ELAPSED_REALTIME, whenElapsed, windowLength, 0);
}
private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, int flags) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, flags);
+ return createAlarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, flags);
+ }
+
+ private static Alarm createAlarmClock(long whenElapsed) {
+ final AlarmManager.AlarmClockInfo info = new AlarmManager.AlarmClockInfo(whenElapsed,
+ mock(PendingIntent.class));
+ return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0,
+ mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE);
}
private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) {
@@ -206,4 +224,21 @@
});
assertEquals(7, mAlarmStore.getNextDeliveryTime());
}
+
+ @Test
+ public void alarmClockRemovalListener() {
+ final Runnable onRemoved = mock(Runnable.class);
+ mAlarmStore.setAlarmClockRemovalListener(onRemoved);
+
+ final Alarm simpleAlarm = createAlarm(5, 0);
+ final Alarm alarmClock = createAlarmClock(10);
+
+ addAlarmsToStore(simpleAlarm, alarmClock);
+
+ mAlarmStore.remove(simpleAlarm::equals);
+ verifyZeroInteractions(onRemoved);
+
+ mAlarmStore.remove(alarmClock::equals);
+ verify(onRemoved).run();
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index 5c2b8ce..b955199 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
@@ -27,17 +27,18 @@
import android.os.Binder;
import android.os.IBinder;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.DisplayPrimaries;
import android.view.SurfaceControl.CieXyz;
+import android.view.SurfaceControl.DisplayPrimaries;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.R;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.R;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -190,6 +191,7 @@
* Matrix should match the precalculated one for given cct and display primaries.
*/
@Test
+ @Ignore
public void displayWhiteBalance_validateTransformMatrix() {
DisplayPrimaries displayPrimaries = new DisplayPrimaries();
displayPrimaries.red = new CieXyz();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index ae9c618..a92357f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertTrue;
+
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -77,14 +79,16 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
- + " <value string-value=\"0\" />"
- + " <value string-value=\"1\" />"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ " </allowed-values>"
- + " <default-value string-value=\"1\" />"
+ + " <default-value int-value=\"1\" />"
+ " </setting>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"false\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -123,14 +127,16 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
- + " <value string-value=\"0\" />"
- + " <value string-value=\"1\" />"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ " </allowed-values>"
- + " <default-value string-value=\"1\" />"
+ + " <default-value int-value=\"1\" />"
+ " </setting>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -152,14 +158,16 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
- + " <value string-value=\"0\" />"
- + " <value string-value=\"1\" />"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ " </allowed-values>"
- + " <default-value string-value=\"1\" />"
+ + " <default-value int-value=\"1\" />"
+ " </setting>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -172,6 +180,7 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"false\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -186,31 +195,32 @@
}
@Test
- public void getAllowedValues_NoMasterXml() {
+ public void isStringValueType_NoMasterXml() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getAllowedValues("foo"));
+ () -> hdmiCecConfig.isStringValueType("foo"));
}
@Test
- public void getAllowedValues_InvalidSetting() {
+ public void isStringValueType_InvalidSetting() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getAllowedValues("foo"));
+ () -> hdmiCecConfig.isStringValueType("foo"));
}
@Test
- public void getAllowedValues_BasicSanity() {
+ public void isStringValueType_BasicSanity() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -220,7 +230,107 @@
+ " <default-value string-value=\"to_tv\" />"
+ " </setting>"
+ "</cec-settings>", null);
- assertThat(hdmiCecConfig.getAllowedValues(
+ assertTrue(hdmiCecConfig.isStringValueType(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ }
+
+ @Test
+ public void isIntValueType_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.isIntValueType("foo"));
+ }
+
+ @Test
+ public void isIntValueType_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.isIntValueType("foo"));
+ }
+
+ @Test
+ public void isIntValueType_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertTrue(hdmiCecConfig.isIntValueType(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
+ }
+
+ @Test
+ public void getAllowedStringValues_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedStringValues("foo"));
+ }
+
+ @Test
+ public void getAllowedStringValues_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedStringValues("foo"));
+ }
+
+ @Test
+ public void getAllowedStringValues_InvalidValueType() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedStringValues(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
+ }
+
+ @Test
+ public void getAllowedStringValues_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
.containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
@@ -228,31 +338,32 @@
}
@Test
- public void getDefaultValue_NoMasterXml() {
+ public void getAllowedIntValues_NoMasterXml() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getDefaultValue("foo"));
+ () -> hdmiCecConfig.getAllowedIntValues("foo"));
}
@Test
- public void getDefaultValue_InvalidSetting() {
+ public void getAllowedIntValues_InvalidSetting() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getDefaultValue("foo"));
+ () -> hdmiCecConfig.getAllowedIntValues("foo"));
}
@Test
- public void getDefaultValue_BasicSanity() {
+ public void getAllowedIntValues_InvalidValueType() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -262,32 +373,199 @@
+ " <default-value string-value=\"to_tv\" />"
+ " </setting>"
+ "</cec-settings>", null);
- assertThat(hdmiCecConfig.getDefaultValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
- .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedIntValues(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
}
@Test
- public void getValue_NoMasterXml() {
+ public void getAllowedIntValues_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllowedIntValues(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
+ .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ }
+
+ @Test
+ public void getDefaultStringValue_NoMasterXml() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getValue("foo"));
+ () -> hdmiCecConfig.getDefaultStringValue("foo"));
}
@Test
- public void getValue_InvalidSetting() {
+ public void getDefaultStringValue_InvalidSetting() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.getValue("foo"));
+ () -> hdmiCecConfig.getDefaultStringValue("foo"));
}
@Test
- public void getValue_GlobalSetting_BasicSanity() {
+ public void getDefaultStringValue_InvalidValueType() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
+ }
+
+ @Test
+ public void getDefaultStringValue_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getDefaultStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ }
+
+ @Test
+ public void getDefaultIntValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultIntValue("foo"));
+ }
+
+ @Test
+ public void getDefaultIntValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultIntValue("foo"));
+ }
+
+ @Test
+ public void getDefaultIntValue_InvalidValueType() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ }
+
+ @Test
+ public void getDefaultIntValue_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getDefaultIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
+ .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ }
+
+ @Test
+ public void getStringValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getStringValue("foo"));
+ }
+
+ @Test
+ public void getStringValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getStringValue("foo"));
+ }
+
+ @Test
+ public void getStringValue_InvalidType() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
+ }
+
+ @Test
+ public void getStringValue_GlobalSetting_BasicSanity() {
when(mStorageAdapter.retrieveGlobalSetting(mContext,
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
@@ -297,6 +575,7 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -306,13 +585,13 @@
+ " <default-value string-value=\"to_tv\" />"
+ " </setting>"
+ "</cec-settings>", null);
- assertThat(hdmiCecConfig.getValue(
+ assertThat(hdmiCecConfig.getStringValue(
HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
.isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
}
@Test
- public void getValue_SystemProperty_BasicSanity() {
+ public void getStringValue_SystemProperty_BasicSanity() {
when(mStorageAdapter.retrieveSystemProperty(
HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiProperties.power_state_change_on_active_source_lost_values
@@ -324,6 +603,7 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"power_state_change_on_active_source_lost\""
+ + " value-type=\"string\""
+ " user-configurable=\"false\">"
+ " <allowed-values>"
+ " <value string-value=\"none\" />"
@@ -332,38 +612,130 @@
+ " <default-value string-value=\"none\" />"
+ " </setting>"
+ "</cec-settings>", null);
- assertThat(hdmiCecConfig.getValue(
+ assertThat(hdmiCecConfig.getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
.isEqualTo(HdmiProperties.power_state_change_on_active_source_lost_values
.STANDBY_NOW.name().toLowerCase());
}
@Test
- public void setValue_NoMasterXml() {
+ public void getIntValue_NoMasterXml() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter, null, null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setValue("foo", "bar"));
+ () -> hdmiCecConfig.getIntValue("foo"));
}
@Test
- public void setValue_InvalidSetting() {
+ public void getIntValue_InvalidSetting() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setValue("foo", "bar"));
+ () -> hdmiCecConfig.getIntValue("foo"));
}
@Test
- public void setValue_NotConfigurable() {
+ public void getIntValue_InvalidType() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ }
+
+ @Test
+ public void getIntValue_GlobalSetting_BasicSanity() {
+ when(mStorageAdapter.retrieveGlobalSetting(mContext,
+ Global.HDMI_CONTROL_ENABLED,
+ Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
+ .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
+ .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ }
+
+ @Test
+ public void getIntValue_SystemProperty_BasicSanity() {
+ when(mStorageAdapter.retrieveSystemProperty(
+ HdmiCecConfig.SYSPROP_SYSTEM_AUDIO_MODE_MUTING,
+ Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED)))
+ .thenReturn(Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"system_audio_mode_muting\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING))
+ .isEqualTo(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
+ }
+
+ @Test
+ public void setStringValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setStringValue("foo", "bar"));
+ }
+
+ @Test
+ public void setStringValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setStringValue("foo", "bar"));
+ }
+
+ @Test
+ public void setStringValue_NotConfigurable() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"false\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -374,18 +746,19 @@
+ " </setting>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setValue(
+ () -> hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
}
@Test
- public void setValue_InvalidValue() {
+ public void setStringValue_InvalidValue() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -396,18 +769,19 @@
+ " </setting>"
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
- () -> hdmiCecConfig.setValue(
+ () -> hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
"bar"));
}
@Test
- public void setValue_GlobalSetting_BasicSanity() {
+ public void setStringValue_GlobalSetting_BasicSanity() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"send_standby_on_sleep\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"to_tv\" />"
@@ -417,7 +791,7 @@
+ " <default-value string-value=\"to_tv\" />"
+ " </setting>"
+ "</cec-settings>", null);
- hdmiCecConfig.setValue(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
verify(mStorageAdapter).storeGlobalSetting(mContext,
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
@@ -425,12 +799,13 @@
}
@Test
- public void setValue_SystemProperty_BasicSanity() {
+ public void setStringValue_SystemProperty_BasicSanity() {
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<cec-settings>"
+ " <setting name=\"power_state_change_on_active_source_lost\""
+ + " value-type=\"string\""
+ " user-configurable=\"true\">"
+ " <allowed-values>"
+ " <value string-value=\"none\" />"
@@ -439,7 +814,7 @@
+ " <default-value string-value=\"none\" />"
+ " </setting>"
+ "</cec-settings>", null);
- hdmiCecConfig.setValue(
+ hdmiCecConfig.setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiProperties.power_state_change_on_active_source_lost_values
.STANDBY_NOW.name().toLowerCase());
@@ -448,4 +823,114 @@
HdmiProperties.power_state_change_on_active_source_lost_values
.STANDBY_NOW.name().toLowerCase());
}
+
+ @Test
+ public void setIntValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter, null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setIntValue("foo", 0));
+ }
+
+ @Test
+ public void setIntValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setIntValue("foo", 0));
+ }
+
+ @Test
+ public void setIntValue_NotConfigurable() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
+ }
+
+ @Test
+ public void setIntValue_InvalidValue() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ 123));
+ }
+
+ @Test
+ public void setIntValue_GlobalSetting_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ verify(mStorageAdapter).storeGlobalSetting(mContext,
+ Global.HDMI_CONTROL_ENABLED,
+ Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
+ }
+
+ @Test
+ public void setIntValue_SystemProperty_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+ mContext, mStorageAdapter,
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"system_audio_mode_muting\""
+ + " value-type=\"int\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value int-value=\"0\" />"
+ + " <value int-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value int-value=\"1\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
+ verify(mStorageAdapter).storeSystemProperty(
+ HdmiCecConfig.SYSPROP_SYSTEM_AUDIO_MODE_MUTING,
+ Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 40d959d..9ba0967 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -44,7 +44,6 @@
public class IncrementalStatesTest {
private IncrementalStates mIncrementalStates;
private ConditionVariable mUnstartableCalled = new ConditionVariable();
- private ConditionVariable mStartableCalled = new ConditionVariable();
private ConditionVariable mFullyLoadedCalled = new ConditionVariable();
private AtomicInteger mUnstartableReason = new AtomicInteger(0);
private static final int WAIT_TIMEOUT_MILLIS = 1000; /* 1 second */
@@ -57,7 +56,6 @@
@Override
public void onPackageStartable() {
- mStartableCalled.open();
}
@Override
@@ -77,24 +75,22 @@
mIncrementalStates.setCallback(mCallback);
mIncrementalStates.onCommit(true);
// Test that package is now startable and loading
- assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
assertTrue(mIncrementalStates.isLoading());
- mStartableCalled.close();
mUnstartableCalled.close();
mFullyLoadedCalled.close();
}
/**
- * Test that startable state changes to false when Incremental Storage is unhealthy.
+ * Test that the package is still startable when Incremental Storage is unhealthy.
*/
@Test
public void testStartableTransition_IncrementalStorageUnhealthy() {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
+ // Test that package is still startable
+ assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+ assertTrue(mIncrementalStates.isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
}
@@ -112,73 +108,34 @@
}
/**
- * Test that the package becomes unstartable when health status indicate storage issues.
+ * Test that the package is still startable when health status indicate storage issues.
*/
@Test
public void testStartableTransition_IncrementalStorageBlocked() {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE,
+ // Test that package is still startable
+ assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+ assertTrue(mIncrementalStates.isStartable());
+ assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
/**
- * Test that the package becomes unstartable when health status indicates transport issues.
+ * Test that the package is still startable when health status indicates transport issues.
*/
@Test
public void testStartableTransition_DataLoaderIntegrityError() {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- assertEquals(PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR,
+ // Test that package is still startable
+ assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+ assertTrue(mIncrementalStates.isStartable());
+ assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
/**
- * Test that the package becomes unstartable when Incremental Storage is unhealthy, and it
- * becomes startable again when Incremental Storage is healthy again.
- */
- @Test
- public void testStartableTransition_IncrementalStorageUnhealthyBackToHealthy()
- throws InterruptedException {
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
- // Test that package is unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
-
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_OK);
- // Test that package is now startable
- assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- }
-
- /**
- * Test that the package becomes unstartable when health status indicates transportation issue,
- * and it becomes startable again when health status is ok again.
- */
- @Test
- public void testStartableTransition_DataLoaderUnhealthyBackToHealthy()
- throws InterruptedException {
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
- // Test that package is unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
-
- mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK);
- // Test that package is now startable
- assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- }
-
- /**
* Test that when loading progress is 1, the package becomes fully loaded, and the change of
* Incremental Storage health status does not affect the startable state.
*/
@@ -197,43 +154,11 @@
}
/**
- * Test that when loading progress is 1, the package becomes fully loaded, and if the package
- * was unstartable, it becomes startable.
- */
- @Test
- public void testLoadingTransition_FullyLoadedWhenUnstartable() throws InterruptedException {
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
- // Test that package is unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- // Test that package is still loading
- assertTrue(mIncrementalStates.isLoading());
-
- mIncrementalStates.setProgress(0.5f);
- // Test that package is still unstartable
- assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- mIncrementalStates.setProgress(1.0f);
- // Test that package is now startable
- assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- // Test that package is now fully loaded
- assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isLoading());
- }
-
- /**
* Test startability transitions if app crashes or anrs
*/
@Test
public void testStartableTransition_AppCrashOrAnr() {
mIncrementalStates.onCrashOrAnr();
- assertFalse(mIncrementalStates.isStartable());
- mIncrementalStates.setProgress(1.0f);
- assertTrue(mIncrementalStates.isStartable());
- mIncrementalStates.onCrashOrAnr();
- // Test that if fully loaded, app remains startable even if it has crashed
assertTrue(mIncrementalStates.isStartable());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 93666b4..cf7f741 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -59,9 +59,9 @@
import static com.android.server.wm.Task.ActivityState.STARTED;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.google.common.truth.Truth.assertThat;
@@ -516,13 +516,13 @@
mActivity.setState(Task.ActivityState.STOPPED, "Testing");
spyOn(mStack);
- doReturn(STACK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
+ doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
- doReturn(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null);
+ doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null);
assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
- doReturn(STACK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null);
+ doReturn(TASK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null);
assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
}
@@ -535,7 +535,7 @@
mActivity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
topActivity.finishing = true;
- doReturn(STACK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
+ doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
assertEquals(false, mActivity.shouldPauseActivity(null /*activeActivity */));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 3c5b9f9..faf4f52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -43,10 +43,10 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
-import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
-import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
+import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getStackAbove;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -123,7 +123,7 @@
final Task destStack = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_STACK_AT_FRONT,
+ mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
@@ -140,7 +140,7 @@
final Task destStack = mDefaultTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
+ mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false, false,
"testResumedActivityFromActivityReparenting");
assertNull(mStack.getResumedActivity());
@@ -418,10 +418,10 @@
assertFalse(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
// Home stack should be visible if one of the halves of split-screen is translucent.
@@ -429,11 +429,11 @@
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
homeStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
final Task splitScreenSecondary2 =
@@ -444,9 +444,9 @@
doReturn(false).when(splitScreenSecondary2).isTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// First split-screen secondary should be visible behind another translucent split-screen
@@ -454,9 +454,9 @@
doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
final Task assistantStack = createStackForShouldBeVisibleTest(
@@ -469,13 +469,13 @@
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
assistantStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen stacks should be visible behind a translucent fullscreen stack.
@@ -484,13 +484,13 @@
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
assistantStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant stack shouldn't be visible behind translucent split-screen stack,
@@ -505,25 +505,25 @@
assertTrue(assistantStack.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
assistantStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
} else {
assertFalse(assistantStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
assistantStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
}
}
@@ -548,33 +548,33 @@
// Re-parent home to split secondary.
homeStack.reparent(splitSecondary, POSITION_TOP);
// Current tasks should be visible.
- assertEquals(STACK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
// Home task should still be visible even though it is a child of another visible task.
- assertEquals(STACK_VISIBILITY_VISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, homeStack.getVisibility(null /* starting */));
// Add fullscreen translucent task that partially occludes split tasks
final Task translucentStack = createStandardStackForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
- assertEquals(STACK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */));
// Split tasks should be visible behind translucent
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitPrimary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitSecondary.getVisibility(null /* starting */));
// Home task should be visible behind translucent since its parent is visible behind
// translucent.
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
homeStack.getVisibility(null /* starting */));
// Hide split-secondary
splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
// Home split secondary and home task should be invisible.
- assertEquals(STACK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */));
}
@Test
@@ -586,9 +586,9 @@
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
}
@@ -604,10 +604,10 @@
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(STACK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
translucentStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
}
@Test
@@ -622,10 +622,10 @@
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(STACK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
opaqueStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
}
@@ -638,9 +638,9 @@
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomTranslucentStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
}
@@ -653,9 +653,9 @@
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(STACK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_VISIBILITY_INVISIBLE,
bottomTranslucentStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */));
}
@Test
@@ -669,15 +669,15 @@
final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomStack.getVisibility(null /* starting */));
- assertEquals(STACK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
// Add an activity to the pinned stack so it isn't considered empty for visibility check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
.setTask(pinnedStack)
.build();
- assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
+ assertEquals(TASK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3bd8c27..a7ced1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -486,7 +486,7 @@
final ActivityStarter starter = prepareStarter(0);
final LockTaskController lockTaskController = mAtm.getLockTaskController();
- doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
+ doReturn(true).when(lockTaskController).isNewTaskLockTaskModeViolation(any());
final int result = starter.setReason("testTaskModeViolation").execute();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5cb3ac1..5641fe2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -83,6 +83,8 @@
public class DragDropControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 3000;
private static final int TEST_UID = 12345;
+ private static final int TEST_PID = 67890;
+ private static final String TEST_PACKAGE = "com.test.package";
private TestDragDropController mTarget;
private WindowState mWindow;
@@ -243,21 +245,16 @@
});
try {
session.validateAndResolveDragMimeTypeExtras(
- createClipDataForActivity(null, null), 0);
- fail("Expected failure without pending intent and user");
- } catch (IllegalArgumentException e) {
- // Expected failure
- }
- try {
- session.validateAndResolveDragMimeTypeExtras(
- createClipDataForActivity(mock(PendingIntent.class), null), 0);
+ createClipDataForActivity(mock(PendingIntent.class), null), TEST_UID, TEST_PID,
+ TEST_PACKAGE);
fail("Expected failure without user");
} catch (IllegalArgumentException e) {
// Expected failure
}
try {
session.validateAndResolveDragMimeTypeExtras(
- createClipDataForActivity(null, mock(UserHandle.class)), 0);
+ createClipDataForActivity(null, mock(UserHandle.class)), TEST_UID, TEST_PID,
+ TEST_PACKAGE);
fail("Expected failure without pending intent");
} catch (IllegalArgumentException e) {
// Expected failure
@@ -286,15 +283,48 @@
public void onAnimatorScaleChanged(float scale) {}
});
try {
- final ClipData clipData = new ClipData(
- new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_SHORTCUT }),
- new ClipData.Item(new Intent()));
-
- session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID);
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForShortcut(null, "test_shortcut_id", mock(UserHandle.class)),
+ TEST_UID, TEST_PID, TEST_PACKAGE);
+ fail("Expected failure without package name");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ try {
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForShortcut("test_package", null, mock(UserHandle.class)),
+ TEST_UID, TEST_PID, TEST_PACKAGE);
fail("Expected failure without shortcut id");
} catch (IllegalArgumentException e) {
// Expected failure
}
+ try {
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForShortcut("test_package", "test_shortcut_id", null),
+ TEST_UID, TEST_PID, TEST_PACKAGE);
+ fail("Expected failure without package name");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ }
+
+ private ClipData createClipDataForShortcut(String packageName, String shortcutId,
+ UserHandle user) {
+ final Intent data = new Intent();
+ if (packageName != null) {
+ data.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ }
+ if (shortcutId != null) {
+ data.putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutId);
+ }
+ if (user != null) {
+ data.putExtra(Intent.EXTRA_USER, user);
+ }
+ final ClipData clipData = new ClipData(
+ new ClipDescription("drag", new String[] {
+ MIMETYPE_APPLICATION_SHORTCUT}),
+ new ClipData.Item(data));
+ return clipData;
}
@Test
@@ -308,7 +338,8 @@
new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }),
new ClipData.Item(new Intent()));
- session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID);
+ session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID, TEST_PID,
+ TEST_PACKAGE);
fail("Expected failure without task id");
} catch (IllegalArgumentException e) {
// Expected failure
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 044f819..bf718a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -50,6 +50,11 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_LOCKED;
import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED;
@@ -169,7 +174,7 @@
@Test
public void testStartLockTaskMode_once() throws Exception {
// GIVEN a task record with allowlisted auth
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
// WHEN calling setLockTaskMode for LOCKED mode without resuming
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
@@ -186,8 +191,8 @@
@Test
public void testStartLockTaskMode_twice() throws Exception {
// GIVEN two task records with allowlisted auth
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
- Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
// WHEN calling setLockTaskMode for LOCKED mode on both tasks
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
@@ -206,7 +211,7 @@
@Test
public void testStartLockTaskMode_pinningRequest() {
// GIVEN a task record that is not allowlisted, i.e. with pinned auth
- Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE);
+ Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
// WHEN calling startLockTaskMode
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
@@ -218,7 +223,7 @@
@Test
public void testStartLockTaskMode_pinnedBySystem() throws Exception {
// GIVEN a task record with pinned auth
- Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE);
+ Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
// WHEN the system calls startLockTaskMode
mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
@@ -237,41 +242,39 @@
@Test
public void testLockTaskViolation() {
// GIVEN one task record with allowlisted auth that is in lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN it's not a lock task violation to try and launch this task without clearing
assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
// THEN it's a lock task violation to launch another task that is not allowlisted
- assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
- Task.LOCK_TASK_AUTH_PINNABLE)));
+ assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(LOCK_TASK_AUTH_PINNABLE)));
// THEN it's a lock task violation to launch another task that is disallowed from lock task
- assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(
- Task.LOCK_TASK_AUTH_DONT_LOCK)));
+ assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(LOCK_TASK_AUTH_DONT_LOCK)));
// THEN it's no a lock task violation to launch another task that is allowlisted
assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
- Task.LOCK_TASK_AUTH_ALLOWLISTED)));
+ LOCK_TASK_AUTH_ALLOWLISTED)));
assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
- Task.LOCK_TASK_AUTH_LAUNCHABLE)));
+ LOCK_TASK_AUTH_LAUNCHABLE)));
// THEN it's not a lock task violation to launch another task that is priv launchable
assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
- Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
+ LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
}
@Test
public void testLockTaskViolation_emergencyCall() {
// GIVEN one task record with allowlisted auth that is in lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// GIVEN tasks necessary for emergency calling
Task keypad = getTask(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
- Task.LOCK_TASK_AUTH_PINNABLE);
+ LOCK_TASK_AUTH_PINNABLE);
Task callAction = getTask(new Intent(Intent.ACTION_CALL_EMERGENCY),
- Task.LOCK_TASK_AUTH_PINNABLE);
- Task dialer = getTask("com.example.dialer", Task.LOCK_TASK_AUTH_PINNABLE);
+ LOCK_TASK_AUTH_PINNABLE);
+ Task dialer = getTask("com.example.dialer", LOCK_TASK_AUTH_PINNABLE);
when(mTelecomManager.getSystemDialerPackage())
.thenReturn(dialer.intent.getComponent().getPackageName());
@@ -295,7 +298,7 @@
@Test
public void testStopLockTaskMode() throws Exception {
// GIVEN one task record with allowlisted auth that is in lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN the same caller calls stopLockTaskMode
@@ -312,7 +315,7 @@
@Test(expected = SecurityException.class)
public void testStopLockTaskMode_differentCaller() {
// GIVEN one task record with allowlisted auth that is in lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN a different caller calls stopLockTaskMode
@@ -324,7 +327,7 @@
@Test
public void testStopLockTaskMode_systemCaller() {
// GIVEN one task record with allowlisted auth that is in lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN system calls stopLockTaskMode
@@ -337,8 +340,8 @@
@Test
public void testStopLockTaskMode_twoTasks() throws Exception {
// GIVEN two task records with allowlisted auth that is in lock task mode
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
- Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
@@ -358,8 +361,8 @@
@Test
public void testStopLockTaskMode_rootTask() throws Exception {
// GIVEN two task records with allowlisted auth that is in lock task mode
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
- Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
@@ -379,7 +382,7 @@
@Test
public void testStopLockTaskMode_pinned() throws Exception {
// GIVEN one task records that is in pinned mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE);
+ Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
// GIVEN that the keyguard is required to show after unlocking
Settings.Secure.putInt(mContext.getContentResolver(),
@@ -406,8 +409,8 @@
@Test
public void testClearLockedTasks() throws Exception {
// GIVEN two task records with allowlisted auth that is in lock task mode
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
- Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
@@ -434,7 +437,7 @@
.thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
// AND there is a task record
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
// WHEN calling clearLockedTasks on the root task
@@ -454,7 +457,7 @@
.thenReturn(true);
// AND there is a task record
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
// WHEN calling clearLockedTasks on the root task
@@ -471,7 +474,7 @@
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
// AND there is a task record
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
// WHEN calling clearLockedTasks on the root task
@@ -488,7 +491,7 @@
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
// AND there is a task record
- Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
// WHEN calling clearLockedTasks on the root task
@@ -574,7 +577,7 @@
@Test
public void testUpdateLockTaskFeatures() throws Exception {
// GIVEN a locked task
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN lock task mode should be started with default status bar masks
@@ -616,7 +619,7 @@
@Test
public void testUpdateLockTaskFeatures_differentUser() throws Exception {
// GIVEN a locked task
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN lock task mode should be started with default status bar masks
@@ -638,7 +641,7 @@
@Test
public void testUpdateLockTaskFeatures_keyguard() {
// GIVEN a locked task
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// THEN keyguard should be disabled
@@ -704,7 +707,7 @@
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
// Start lock task mode
- Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED);
+ Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
@@ -758,14 +761,13 @@
* @param isAppAware {@code true} if the app has marked if allowlisted in its manifest
*/
private Task getTaskForUpdate(String pkg, boolean isAppAware) {
- final int authIfAllowlisted = isAppAware
- ? Task.LOCK_TASK_AUTH_LAUNCHABLE
- : Task.LOCK_TASK_AUTH_ALLOWLISTED;
+ final int authIfAllowlisted =
+ isAppAware ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_ALLOWLISTED;
Task tr = getTask(pkg, authIfAllowlisted);
doAnswer((invocation) -> {
boolean isAllowlisted =
mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg);
- tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE;
+ tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : LOCK_TASK_AUTH_PINNABLE;
return null;
}).when(tr).setLockTaskAuth();
return tr;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 1879e9e..4b8bbc1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -38,7 +38,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -107,7 +107,7 @@
public void testRestoringInvalidTask() {
mRootWindowContainer.getDefaultDisplay().removeAllTasks();
Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+ MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
assertNull(task);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 9304dc5..5c4563e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -62,15 +62,18 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.when;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.Size;
import android.view.DisplayCutout;
+import android.view.InputWindowHandle;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -464,10 +467,10 @@
public void testDisplayIdUpdatedOnReparent() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
// fake a different display
- app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1;
+ app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1);
app.onDisplayChanged(mDisplayContent);
- assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId()));
+ assertThat(app.mInputWindowHandle.getDisplayId(), is(mDisplayContent.getDisplayId()));
assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId()));
}
@@ -678,6 +681,54 @@
assertFalse(win0.canReceiveTouchInput());
}
+ @Test
+ public void testUpdateInputWindowHandle() {
+ final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+ win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ final InputWindowHandle handle = new InputWindowHandle(
+ win.mInputWindowHandle.getInputApplicationHandle(), win.getDisplayId());
+ final InputWindowHandleWrapper handleWrapper = new InputWindowHandleWrapper(handle);
+ final IBinder inputChannelToken = mock(IBinder.class);
+ win.mInputChannelToken = inputChannelToken;
+
+ mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win);
+
+ assertTrue(handleWrapper.isChanged());
+ assertEquals(inputChannelToken, handle.token);
+ assertEquals(win.mActivityRecord.getInputApplicationHandle(false /* update */),
+ handle.inputApplicationHandle);
+ assertEquals(win.mAttrs.inputFeatures, handle.inputFeatures);
+ assertEquals(win.isVisible(), handle.visible);
+
+ final SurfaceControl sc = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction transaction = mSystemServicesTestRule.mTransaction;
+ InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper);
+
+ // The fields of input window handle are changed, so it must set input window info
+ // successfully. And then the changed flag should be reset.
+ verify(transaction).setInputWindowInfo(eq(sc), eq(handle));
+ assertFalse(handleWrapper.isChanged());
+ // Populate the same states again, the handle should not detect change.
+ mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win);
+ assertFalse(handleWrapper.isChanged());
+
+ // Apply the no change handle, the invocation of setInputWindowInfo should be skipped.
+ clearInvocations(transaction);
+ InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper);
+ verify(transaction, never()).setInputWindowInfo(any(), any());
+
+ // Populate as an overlay to disable the input of window.
+ InputMonitor.populateOverlayInputInfo(handleWrapper, false /* isVisible */);
+ // The overlay attributes should be set.
+ assertTrue(handleWrapper.isChanged());
+ assertFalse(handle.focusable);
+ assertFalse(handle.visible);
+ assertNull(handle.token);
+ assertEquals(0L, handle.dispatchingTimeoutMillis);
+ assertEquals(WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL,
+ handle.inputFeatures);
+ }
+
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testNeedsRelativeLayeringToIme_notAttached() {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a85eb53..1238e7b 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -1468,8 +1468,11 @@
/**
* Writes the string {@param input} into the outgoing text stream for this RTT call. Since
- * RTT transmits text in real-time, this method should be called once for each character
- * the user enters into the device.
+ * RTT transmits text in real-time, this method should be called once for each user action.
+ * For example, when the user enters text as discrete characters using the keyboard, this
+ * method should be called once for each character. However, if the user enters text by
+ * pasting or autocomplete, the entire contents of the pasted or autocompleted text should
+ * be sent in one call to this method.
*
* This method is not thread-safe -- calling it from multiple threads simultaneously may
* lead to interleaved text.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 7f87019..83e63ef 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1365,6 +1365,7 @@
* include those that were inserted before, maybe empty but not null.
* @hide
*/
+ @NonNull
@UnsupportedAppUsage
public List<SubscriptionInfo> getAllSubscriptionInfoList() {
if (VDBG) logd("[getAllSubscriptionInfoList]+");
@@ -1382,7 +1383,7 @@
}
if (result == null) {
- result = new ArrayList<>();
+ result = Collections.emptyList();
}
return result;
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 3e2a6ee..b054dfc 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -240,6 +240,12 @@
*/
@Deprecated
public int getSuggestedRetryTime() {
+ // To match the pre-deprecated getSuggestedRetryTime() behavior.
+ if (mSuggestedRetryTime == RETRY_INTERVAL_UNDEFINED) {
+ return 0;
+ } else if (mSuggestedRetryTime > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ }
return (int) mSuggestedRetryTime;
}
diff --git a/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl b/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl
new file mode 100644
index 0000000..bbab548
--- /dev/null
+++ b/telephony/java/android/telephony/ims/AudioCodecAttributes.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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 android.telephony.ims;
+
+parcelable AudioCodecAttributes;
diff --git a/telephony/java/android/telephony/ims/AudioCodecAttributes.java b/telephony/java/android/telephony/ims/AudioCodecAttributes.java
new file mode 100644
index 0000000..7b6ab00
--- /dev/null
+++ b/telephony/java/android/telephony/ims/AudioCodecAttributes.java
@@ -0,0 +1,131 @@
+/*
+ * 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 android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Range;
+
+/**
+ * Parcelable object to handle audio codec attributes.
+ * It provides the audio codec bitrate, bandwidth and their upper/lower bound.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AudioCodecAttributes implements Parcelable {
+ // The audio codec bitrate in kbps.
+ private float mBitrateKbps;
+ // The range of the audio codec bitrate in kbps.
+ private Range<Float> mBitrateRangeKbps;
+ // The audio codec bandwidth in kHz.
+ private float mBandwidthKhz;
+ // The range of the audio codec bandwidth in kHz.
+ private Range<Float> mBandwidthRangeKhz;
+
+
+ /**
+ * Constructor.
+ *
+ * @param bitrateKbps The audio codec bitrate in kbps.
+ * @param bitrateRangeKbps The range of the audio codec bitrate in kbps.
+ * @param bandwidthKhz The audio codec bandwidth in kHz.
+ * @param bandwidthRangeKhz The range of the audio codec bandwidth in kHz.
+ */
+
+ public AudioCodecAttributes(float bitrateKbps, @NonNull Range<Float> bitrateRangeKbps,
+ float bandwidthKhz, @NonNull Range<Float> bandwidthRangeKhz) {
+ mBitrateKbps = bitrateKbps;
+ mBitrateRangeKbps = bitrateRangeKbps;
+ mBandwidthKhz = bandwidthKhz;
+ mBandwidthRangeKhz = bandwidthRangeKhz;
+ }
+
+ private AudioCodecAttributes(Parcel in) {
+ mBitrateKbps = in.readFloat();
+ mBitrateRangeKbps = new Range<>(in.readFloat(), in.readFloat());
+ mBandwidthKhz = in.readFloat();
+ mBandwidthRangeKhz = new Range<>(in.readFloat(), in.readFloat());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeFloat(mBitrateKbps);
+ out.writeFloat(mBitrateRangeKbps.getLower());
+ out.writeFloat(mBitrateRangeKbps.getUpper());
+ out.writeFloat(mBandwidthKhz);
+ out.writeFloat(mBandwidthRangeKhz.getLower());
+ out.writeFloat(mBandwidthRangeKhz.getUpper());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<AudioCodecAttributes> CREATOR =
+ new Creator<AudioCodecAttributes>() {
+ @Override
+ public AudioCodecAttributes createFromParcel(Parcel in) {
+ return new AudioCodecAttributes(in);
+ }
+
+ @Override
+ public AudioCodecAttributes[] newArray(int size) {
+ return new AudioCodecAttributes[size];
+ }
+ };
+
+ /**
+ * @return the exact value of the audio codec bitrate in kbps.
+ */
+ public float getBitrateKbps() {
+ return mBitrateKbps;
+ }
+
+ /**
+ * @return the range of the audio codec bitrate in kbps
+ */
+ public @NonNull Range<Float> getBitrateRangeKbps() {
+ return mBitrateRangeKbps;
+ }
+
+ /**
+ * @return the exact value of the audio codec bandwidth in kHz.
+ */
+ public float getBandwidthKhz() {
+ return mBandwidthKhz;
+ }
+
+ /**
+ * @return the range of the audio codec bandwidth in kHz.
+ */
+ public @NonNull Range<Float> getBandwidthRangeKhz() {
+ return mBandwidthRangeKhz;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ bitrateKbps=" + mBitrateKbps
+ + ", bitrateRangeKbps=" + mBitrateRangeKbps
+ + ", bandwidthKhz=" + mBandwidthKhz
+ + ", bandwidthRangeKhz=" + mBandwidthRangeKhz + " }";
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 2792f79..d924bae 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -17,6 +17,7 @@
package android.telephony.ims;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -90,6 +91,9 @@
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int mAudioDirection;
+ // Audio codec attributes
+ private AudioCodecAttributes mAudioCodecAttributes;
+
// Video related information
/** @hide */
public int mVideoQuality;
@@ -191,6 +195,7 @@
public void copyFrom(ImsStreamMediaProfile profile) {
mAudioQuality = profile.mAudioQuality;
mAudioDirection = profile.mAudioDirection;
+ mAudioCodecAttributes = profile.mAudioCodecAttributes;
mVideoQuality = profile.mVideoQuality;
mVideoDirection = profile.mVideoDirection;
mRttMode = profile.mRttMode;
@@ -199,12 +204,13 @@
@NonNull
@Override
public String toString() {
- return "{ audioQuality=" + mAudioQuality +
- ", audioDirection=" + mAudioDirection +
- ", videoQuality=" + mVideoQuality +
- ", videoDirection=" + mVideoDirection +
- ", rttMode=" + mRttMode +
- ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
+ return "{ audioQuality=" + mAudioQuality
+ + ", audioDirection=" + mAudioDirection
+ + ", audioCodecAttribute=" + mAudioCodecAttributes
+ + ", videoQuality=" + mVideoQuality
+ + ", videoDirection=" + mVideoDirection
+ + ", rttMode=" + mRttMode
+ + ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
}
@Override
@@ -216,6 +222,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mAudioQuality);
out.writeInt(mAudioDirection);
+ out.writeTypedObject(mAudioCodecAttributes, flags);
out.writeInt(mVideoQuality);
out.writeInt(mVideoDirection);
out.writeInt(mRttMode);
@@ -225,6 +232,7 @@
private void readFromParcel(Parcel in) {
mAudioQuality = in.readInt();
mAudioDirection = in.readInt();
+ mAudioCodecAttributes = in.readTypedObject(AudioCodecAttributes.CREATOR);
mVideoQuality = in.readInt();
mVideoDirection = in.readInt();
mRttMode = in.readInt();
@@ -275,6 +283,23 @@
return mAudioDirection;
}
+ /**
+ * Get the audio codec attributes {@link AudioCodecAttributes} which may be {@code null} if
+ * ImsService doesn't support this information.
+ * @return audio codec attributes
+ */
+ public @Nullable AudioCodecAttributes getAudioCodecAttributes() {
+ return mAudioCodecAttributes;
+ }
+
+ /**
+ * Set the audio codec attributes {@link AudioCodecAttributes} which includes bitrate and
+ * bandwidth information.
+ */
+ public void setAudioCodecAttributes(@NonNull AudioCodecAttributes audioCodecAttributes) {
+ mAudioCodecAttributes = audioCodecAttributes;
+ }
+
public int getVideoQuality() {
return mVideoQuality;
}
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
index 1adbc2d..322bbff 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -32,7 +32,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -98,8 +98,8 @@
};
@DataClass.Generated(
- time = 1603836848866L,
- codegenVersion = "1.0.18",
+ time = 1604435620553L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
index a4fdcd1..a8ae72d 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -46,7 +46,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -120,8 +120,8 @@
};
@DataClass.Generated(
- time = 1603836849753L,
- codegenVersion = "1.0.18",
+ time = 1604435621500L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
index f0d728e..ca4278d 100644
--- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -54,7 +54,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -412,8 +412,8 @@
}
@DataClass.Generated(
- time = 1603836847927L,
- codegenVersion = "1.0.18",
+ time = 1604435619612L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index a3f458b..ce1e043 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -344,7 +344,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -1874,8 +1874,8 @@
}
@DataClass.Generated(
- time = 1603836845952L,
- codegenVersion = "1.0.18",
+ time = 1604435617581L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final int STATE_UNDEFINED\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index e356704..5bbbf41 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -85,7 +85,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -253,8 +253,8 @@
}
@DataClass.Generated(
- time = 1603836846970L,
- codegenVersion = "1.0.18",
+ time = 1604435618584L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
index 07ec31d..c762164 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java
@@ -36,7 +36,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -135,8 +135,8 @@
};
@DataClass.Generated(
- time = 1603836851627L,
- codegenVersion = "1.0.18",
+ time = 1604435623368L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
inputSignatures = " @android.annotation.NonNull java.lang.String mBar\nclass NestedDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
@Deprecated
@@ -160,7 +160,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -259,8 +259,8 @@
};
@DataClass.Generated(
- time = 1603836851635L,
- codegenVersion = "1.0.18",
+ time = 1604435623377L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
inputSignatures = " @android.annotation.NonNull long mBaz2\nclass NestedDataClass3 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
@Deprecated
@@ -274,7 +274,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -373,8 +373,8 @@
};
@DataClass.Generated(
- time = 1603836851640L,
- codegenVersion = "1.0.18",
+ time = 1604435623381L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithNestedDataClasses.java",
inputSignatures = " @android.annotation.NonNull java.lang.String mBaz\nclass NestedDataClass2 extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
index 5cbc6b3..0813dbe 100644
--- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -59,7 +59,7 @@
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.19.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -84,8 +84,8 @@
}
@DataClass.Generated(
- time = 1603836850677L,
- codegenVersion = "1.0.18",
+ time = 1604435622426L,
+ codegenVersion = "1.0.19",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
inputSignatures = "private @android.annotation.Nullable java.util.List<java.util.Set<?>> mUsesWildcards\npublic @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
@Deprecated
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 6b974ff..db0eed8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -45,6 +45,15 @@
}
}
+fun WmAssertion.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("visibleWindowShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+}
+
@JvmOverloads
fun LayersAssertion.noUncoveredRegions(
beginRotation: Int,
@@ -159,6 +168,15 @@
}
}
+fun LayersAssertion.visibleLayersShownMoreThanOneConsecutiveEntry(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+}
+
fun EventLogAssertion.focusChanges(
vararg windows: String,
bugId: Int = 0,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index d31c4ba..72efdb1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -43,12 +43,12 @@
}
}
-fun LayersAssertion.wallpaperLayerBecomesInvisible(
+fun LayersAssertion.appLayerReplacesWallpaperLayer(
testApp: IAppHelper,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("wallpaperLayerBecomesInvisible", bugId, enabled) {
+ all("appLayerReplacesWallpaperLayer", bugId, enabled) {
this.showsLayer("Wallpaper")
.then()
.replaceVisibleLayer("Wallpaper", testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index ad23d9f..1f03c4d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -38,6 +38,8 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -87,6 +89,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
appWindowReplacesLauncherAsTopWindow(testApp)
wallpaperWindowBecomesInvisible()
@@ -102,8 +105,10 @@
configuration.endRotation)
navBarLayerIsAlwaysVisible(enabled = false)
statusBarLayerIsAlwaysVisible(enabled = false)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ enabled = Surface.ROTATION_0 == configuration.endRotation)
- wallpaperLayerBecomesInvisible(testApp)
+ appLayerReplacesWallpaperLayer(testApp)
}
eventLog {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
new file mode 100644
index 0000000..1833874
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.server.wm.flicker.launch
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.reopenAppFromOverview
+import com.android.server.wm.flicker.helpers.hasWindow
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Launch an app from the recents app view (the overview)
+ * To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppFromOverviewTest(
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ return FlickerTestRunnerFactory(instrumentation, repetitions = 10)
+ .buildTest { configuration ->
+ withTag { buildTestTag("openAppFromOverview", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.open()
+ }
+ eachRun {
+ device.pressHome()
+ device.pressRecentApps()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ device.reopenAppFromOverview()
+ device.hasWindow(testApp.getPackage())
+ }
+ teardown {
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 5886a61..9b4223a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -38,6 +38,8 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -91,9 +93,10 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible(enabled = false)
+ wallpaperWindowBecomesInvisible()
}
layersTrace {
@@ -106,8 +109,10 @@
configuration.endRotation)
navBarLayerIsAlwaysVisible(enabled = false)
statusBarLayerIsAlwaysVisible(enabled = false)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ enabled = Surface.ROTATION_0 == configuration.endRotation)
- wallpaperLayerBecomesInvisible(testApp)
+ appLayerReplacesWallpaperLayer(testApp)
}
eventLog {
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
new file mode 100644
index 0000000..f967bf0
--- /dev/null
+++ b/tests/vcn/Android.bp
@@ -0,0 +1,27 @@
+//########################################################################
+// Build FrameworksVcnTests package
+//########################################################################
+
+android_test {
+ name: "FrameworksVcnTests",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ certificate: "platform",
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "framework-protos",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "services.core",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+}
diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml
new file mode 100644
index 0000000..2ad9aac
--- /dev/null
+++ b/tests/vcn/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.tests.vcn">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.tests.vcn"
+ android:label="Frameworks VCN Tests" />
+</manifest>
diff --git a/tests/vcn/AndroidTest.xml b/tests/vcn/AndroidTest.xml
new file mode 100644
index 0000000..dc521fd
--- /dev/null
+++ b/tests/vcn/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VCN Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksVcnTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksVcnTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.vcn" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/vcn/TEST_MAPPING b/tests/vcn/TEST_MAPPING
new file mode 100644
index 0000000..54fa411
--- /dev/null
+++ b/tests/vcn/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksVcnTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt
index bf95a2e..056898c 100644
--- a/tools/codegen/src/com/android/codegen/ClassInfo.kt
+++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt
@@ -1,12 +1,14 @@
package com.android.codegen
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
open class ClassInfo(val classAst: ClassOrInterfaceDeclaration, val fileInfo: FileInfo) {
val fileAst = fileInfo.fileAst
val nestedClasses = classAst.members.filterIsInstance<ClassOrInterfaceDeclaration>()
+ val nestedTypes = classAst.members.filterIsInstance<TypeDeclaration<*>>()
val superInterfaces = classAst.implementedTypes.map { it.asString() }
val superClass = classAst.extendedTypes.getOrNull(0)
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 69ff18d..1aea575 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -128,7 +128,7 @@
if (classAst.nameAsString == className) return thisPackagePrefix + classAst.nameAsString
- nestedClasses.find {
+ nestedTypes.find {
it.nameAsString == className
}?.let { return thisClassPrefix + it.nameAsString }
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index ca658a9..147f18c 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.18"
+const val CODEGEN_VERSION = "1.0.19"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 2759e29..15b8b41 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -16,18 +16,25 @@
static_libs: [
"libbase",
- "libbinder",
"libinput",
"libutils",
"libcutils",
"liblog",
"libui-types",
],
+ target: {
+ linux_glibc: {
+ static_libs: [
+ // libbinder is only available for linux
+ "libbinder",
+ ],
+ },
+ },
// This tool is prebuilt if we're doing an app-only build.
product_variables: {
unbundled_build: {
- enabled: false,
+ enabled: false,
},
},
}
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 0af6266..3865076 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -105,8 +105,8 @@
}
case FILETYPE_KEYCHARACTERMAP: {
- base::Result<std::shared_ptr<KeyCharacterMap>> ret = KeyCharacterMap::load(filename,
- KeyCharacterMap::FORMAT_ANY);
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::load(filename, KeyCharacterMap::Format::ANY);
if (!ret) {
error("Error %s parsing key character map file.\n\n", ret.error().message().c_str());
return false;
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index d5ef703..5c4e615 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -105,6 +105,7 @@
field @Deprecated public String FQDN;
field @Deprecated public static final int SECURITY_TYPE_EAP = 3; // 0x3
field @Deprecated public static final int SECURITY_TYPE_EAP_SUITE_B = 5; // 0x5
+ field @Deprecated public static final int SECURITY_TYPE_EAP_WPA3_ENTERPRISE = 9; // 0x9
field @Deprecated public static final int SECURITY_TYPE_OPEN = 0; // 0x0
field @Deprecated public static final int SECURITY_TYPE_OWE = 6; // 0x6
field @Deprecated public static final int SECURITY_TYPE_PSK = 2; // 0x2
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 9298c1e..34a6938 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -447,6 +447,8 @@
public static final int SECURITY_TYPE_WAPI_PSK = 7;
/** Security type for a WAPI Certificate network. */
public static final int SECURITY_TYPE_WAPI_CERT = 8;
+ /** Security type for a WPA3-Enterprise network. */
+ public static final int SECURITY_TYPE_EAP_WPA3_ENTERPRISE = 9;
/**
* Security types we support.
@@ -462,7 +464,8 @@
SECURITY_TYPE_EAP_SUITE_B,
SECURITY_TYPE_OWE,
SECURITY_TYPE_WAPI_PSK,
- SECURITY_TYPE_WAPI_CERT
+ SECURITY_TYPE_WAPI_CERT,
+ SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
})
public @interface SecurityType {}
@@ -478,8 +481,9 @@
* {@link #SECURITY_TYPE_SAE},
* {@link #SECURITY_TYPE_EAP_SUITE_B},
* {@link #SECURITY_TYPE_OWE},
- * {@link #SECURITY_TYPE_WAPI_PSK}, or
- * {@link #SECURITY_TYPE_WAPI_CERT}
+ * {@link #SECURITY_TYPE_WAPI_PSK},
+ * {@link #SECURITY_TYPE_WAPI_CERT},
+ * {@link #SECURITY_TYPE_EAP_WPA3_ENTERPRISE}
*/
public void setSecurityParams(@SecurityType int securityType) {
// Clear all the bitsets.
@@ -555,6 +559,16 @@
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4);
break;
+ case SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
+ allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+ allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+ requirePmf = true;
+ break;
default:
throw new IllegalArgumentException("unknown security type " + securityType);
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 35853c0..f5ffd93 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -373,14 +373,8 @@
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
} else {
// WPA3-Enterprise
- configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
- configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
- configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
- configuration.allowedPairwiseCiphers.set(
- WifiConfiguration.PairwiseCipher.GCMP_256);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
- configuration.requirePmf = true;
+ configuration.setSecurityParams(
+ WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
}
configuration.enterpriseConfig = mWpa3EnterpriseConfig;
} else if (mIsEnhancedOpen) { // OWE network
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index dc6ec90..dc51897 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -802,14 +802,8 @@
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
} else {
// WPA3-Enterprise
- configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
- configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
- configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
- configuration.allowedPairwiseCiphers.set(
- WifiConfiguration.PairwiseCipher.GCMP_256);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
- configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
- configuration.requirePmf = true;
+ configuration.setSecurityParams(
+ WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
}
configuration.enterpriseConfig = mWpa3EnterpriseConfig;
} else if (mIsEnhancedOpen) { // OWE network
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index f09c37d..0a80dc2 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,6 +18,7 @@
import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP;
import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OPEN;
import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE;
import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK;
@@ -581,6 +582,26 @@
}
/**
+ * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the
+ * {@link WifiConfiguration} object correctly for WPA3 Enterprise security type.
+ * @throws Exception
+ */
+ @Test
+ public void testSetSecurityParamsForWpa3Enterprise() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.setSecurityParams(SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
+ assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.GCMP_256));
+ assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(config.requirePmf);
+ }
+
+ /**
* Test that the NetworkSelectionStatus Builder returns the same values that was set, and that
* calling build multiple times returns different instances.
*/
@@ -641,6 +662,9 @@
configuration.setSecurityParams(SECURITY_TYPE_EAP);
assertFalse(configuration.needsPreSharedKey());
+ configuration.setSecurityParams(SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+ assertFalse(configuration.needsPreSharedKey());
+
configuration.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
assertFalse(configuration.needsPreSharedKey());
}
@@ -667,6 +691,9 @@
configuration.setSecurityParams(SECURITY_TYPE_EAP);
assertEquals(KeyMgmt.WPA_EAP, configuration.getAuthType());
+ configuration.setSecurityParams(SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+ assertEquals(KeyMgmt.WPA_EAP, configuration.getAuthType());
+
configuration.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
assertEquals(KeyMgmt.SUITE_B_192, configuration.getAuthType());