Merge "Avoid unwanted wakeAndUnLock mode for face" into qt-r1-dev
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index a8b99ce..a527263 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -564,7 +564,8 @@
int skipped[] = SKIPPED_SECTIONS;
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
- if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+ if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
+ && !section_requires_specific_mention(id)) {
incidentArgs.addSection(id);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3f7b291..507e97e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -275,7 +275,6 @@
* <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
* </ul>
*
* <p>When device owner provisioning has completed, an intent of the type
@@ -383,7 +382,6 @@
* <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_USE_MOBILE_DATA}, optional </li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
* </ul>
*
* @hide
@@ -423,7 +421,6 @@
* <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
* <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}, optional</li>
* </ul>
*
* <p>When device owner provisioning has completed, an intent of the type
@@ -1149,11 +1146,11 @@
* A boolean extra indicating if the education screens from the provisioning flow should be
* skipped. If unspecified, defaults to {@code false}.
*
+ * <p>This extra can only be set by the admin app when performing the admin-integrated
+ * provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ *
* <p>If the education screens are skipped, it is the admin application's responsibility
* to display its own user education screens.
- *
- * <p>It can be used when provisioning a fully managed device via
- * {@link #ACTION_PROVISION_MANAGED_DEVICE}.
*/
public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS =
"android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
@@ -2263,6 +2260,10 @@
* <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li>
* </ul>
*
+ * <p>If performing fully-managed device provisioning and the admin app desires to show its
+ * own education screens, the target activity can additionally return
+ * {@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS} set to <code>true</code>.
+ *
* <p>The target activity may also return the account that needs to be migrated from primary
* user to managed profile in case of a profile owner provisioning in
* {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result.
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 5ff627f..12edc5e 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -119,12 +119,50 @@
public static final int TYPE_VEC3_CALIBRATION = 0x10002;
/**
- * Sensor placement. Describes location and installation angle of the sensor device.
+ * Sensor placement.
*
- * Payload:
- * floatValues[0..11]: First 3 rows of homogeneous matrix in row major order that describes
- * the location and orientation of the sensor. Origin of reference will be the mobile device
- * geometric sensor. Reference frame is defined as the same as Android sensor frame.
+ * Provides the orientation and location of the sensor element in terms of the
+ * Android coordinate system. This data is given as a 3x4 matrix consisting of a 3x3 rotation
+ * matrix (R) concatenated with a 3x1 location vector (t). The rotation matrix provides the
+ * orientation of the Android device coordinate frame relative to the local coordinate frame of
+ * the sensor. Note that assuming the axes conventions of the sensor are the same as Android,
+ * this is the inverse of the matrix applied to raw samples read from the sensor to convert them
+ * into the Android representation. The location vector represents the translation from the
+ * origin of the Android sensor coordinate system to the geometric center of the sensor,
+ * specified in millimeters (mm).
+ * <p>
+ * <b>Payload</b>:
+ * <code>floatValues[0..11]</code>: 3x4 matrix in row major order [R; t]
+ * </p>
+ * <p>
+ * <b>Example</b>:
+ * This raw buffer: <code>{0, 1, 0, 0, -1, 0, 0, 10, 0, 0, 1, -2.5}</code><br>
+ * Corresponds to this 3x4 matrix:
+ * <table>
+ * <thead>
+ * <tr><td colspan="3">Orientation</td><td>Location</tr>
+ * </thead>
+ * <tbody>
+ * <tr><td>0</td><td>1</td><td>0</td><td>0</td></tr>
+ * <tr><td>-1</td><td>0</td><td>0</td><td>10</td></tr>
+ * <tr><td>0</td><td>0</td><td>1</td><td>-2.5</td></tr>
+ * </tbody>
+ * </table>
+ * The sensor is oriented such that:
+ * <ul>
+ * <li>The device X axis corresponds to the sensor's local -Y axis
+ * <li>The device Y axis corresponds to the sensor's local X axis
+ * <li>The device Z axis and sensor's local Z axis are equivalent
+ * </ul>
+ * In other words, if viewing the origin of the Android coordinate system from the positive
+ * Z direction, the device coordinate frame is to be rotated 90° counter-clockwise about the
+ * Z axis to align with the sensor's local coordinate frame. Equivalently, a vector in the
+ * Android coordinate frame may be multiplied with R to rotate it 90° clockwise (270°
+ * counter-clockwise), yielding its representation in the sensor's coordinate frame.
+ * Relative to the origin of the Android coordinate system, the physical center of the
+ * sensor is located 10mm in the positive Y direction, and 2.5mm in the negative Z
+ * direction.
+ * </p>
*/
public static final int TYPE_SENSOR_PLACEMENT = 0x10003;
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 035061b..702b41b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -330,18 +330,25 @@
@TestApi
@Nullable
public static VibrationEffect get(Uri uri, Context context) {
+ final ContentResolver cr = context.getContentResolver();
+ Uri uncanonicalUri = cr.uncanonicalize(uri);
+ if (uncanonicalUri == null) {
+ // If we already had an uncanonical URI, it's possible we'll get null back here. In
+ // this case, just use the URI as passed in since it wasn't canonicalized in the first
+ // place.
+ uncanonicalUri = uri;
+ }
String[] uris = context.getResources().getStringArray(
com.android.internal.R.array.config_ringtoneEffectUris);
for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
if (uris[i] == null) {
continue;
}
- ContentResolver cr = context.getContentResolver();
Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
if (mappedUri == null) {
continue;
}
- if (mappedUri.equals(uri)) {
+ if (mappedUri.equals(uncanonicalUri)) {
return get(RINGTONES[i]);
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a01beb..9573ac0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -398,6 +398,8 @@
mSystemWindowInsets.bottom));
((ListView) mAdapterView).addFooterView(mFooterSpacer);
+ resetButtonBar();
+
return insets.consumeSystemWindowInsets();
}
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index d9fd3b5..a6286c0 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -53,11 +53,13 @@
protected WallpaperColors mLockColors;
public ColorExtractor(Context context) {
- this(context, new Tonal(context), true /* immediately */);
+ this(context, new Tonal(context), true /* immediately */,
+ context.getSystemService(WallpaperManager.class));
}
@VisibleForTesting
- public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately) {
+ public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately,
+ WallpaperManager wallpaperManager) {
mContext = context;
mExtractionType = extractionType;
@@ -71,14 +73,8 @@
}
mOnColorsChangedListeners = new ArrayList<>();
-
- WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
- if (wallpaperManager == null) {
- Log.w(TAG, "Can't listen to color changes!");
- } else {
- wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
- initExtractColors(wallpaperManager, immediately);
- }
+ wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
+ initExtractColors(wallpaperManager, immediately);
}
private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
new file mode 100644
index 0000000..0c93f7f
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -0,0 +1,147 @@
+/*
+ * 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.internal.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Multiplexes multiple binder death recipients on the same binder objects, so that at the native
+ * level, we only need to keep track of one death recipient reference. This will help reduce the
+ * number of JNI strong references.
+ *
+ * test with: atest FrameworksCoreTests:BinderDeathDispatcherTest
+ */
+public class BinderDeathDispatcher<T extends IInterface> {
+ private static final String TAG = "BinderDeathDispatcher";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, RecipientsInfo> mTargets = new ArrayMap<>();
+
+ @VisibleForTesting
+ class RecipientsInfo implements DeathRecipient {
+ final IBinder mTarget;
+
+ /**
+ * Recipient list. If it's null, {@link #mTarget} has already died, but in that case
+ * this RecipientsInfo instance is removed from {@link #mTargets}.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ ArraySet<DeathRecipient> mRecipients = new ArraySet<>();
+
+ private RecipientsInfo(IBinder target) {
+ mTarget = target;
+ }
+
+ @Override
+ public void binderDied() {
+ final ArraySet<DeathRecipient> copy;
+ synchronized (mLock) {
+ copy = mRecipients;
+ mRecipients = null;
+
+ // Also remove from the targets.
+ mTargets.remove(mTarget);
+ }
+ if (copy == null) {
+ return;
+ }
+ // Let's call it without holding the lock.
+ final int size = copy.size();
+ for (int i = 0; i < size; i++) {
+ copy.valueAt(i).binderDied();
+ }
+ }
+ }
+
+ /**
+ * Add a {@code recipient} to the death recipient list on {@code target}.
+ *
+ * @return # of recipients in the recipient list, including {@code recipient}. Or, -1
+ * if {@code target} is already dead, in which case recipient's
+ * {@link DeathRecipient#binderDied} won't be called.
+ */
+ public int linkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+ final IBinder ib = target.asBinder();
+ synchronized (mLock) {
+ RecipientsInfo info = mTargets.get(ib);
+ if (info == null) {
+ info = new RecipientsInfo(ib);
+
+ // First recipient; need to link to death.
+ try {
+ ib.linkToDeath(info, 0);
+ } catch (RemoteException e) {
+ return -1; // Already dead.
+ }
+ mTargets.put(ib, info);
+ }
+ info.mRecipients.add(recipient);
+ return info.mRecipients.size();
+ }
+ }
+
+ public void unlinkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
+ final IBinder ib = target.asBinder();
+
+ synchronized (mLock) {
+ final RecipientsInfo info = mTargets.get(ib);
+ if (info == null) {
+ return;
+ }
+ if (info.mRecipients.remove(recipient) && info.mRecipients.size() == 0) {
+ info.mTarget.unlinkToDeath(info, 0);
+ mTargets.remove(info.mTarget);
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw, String indent) {
+ synchronized (mLock) {
+ pw.print(indent);
+ pw.print("# of watched binders: ");
+ pw.println(mTargets.size());
+
+ pw.print(indent);
+ pw.print("# of death recipients: ");
+ int n = 0;
+ for (RecipientsInfo info : mTargets.values()) {
+ n += info.mRecipients.size();
+ }
+ pw.println(n);
+ }
+ }
+
+ @VisibleForTesting
+ public ArrayMap<IBinder, RecipientsInfo> getTargetsForTest() {
+ return mTargets;
+ }
+}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index e8691fa..b73ecd1 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -756,4 +756,8 @@
return String.valueOf(value);
}
}
+
+ public static @Nullable <T> T firstOrNull(T[] items) {
+ return items.length > 0 ? items[0] : null;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
new file mode 100644
index 0000000..5914887
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BinderDeathDispatcherTest {
+ private static class MyTarget implements IInterface, IBinder {
+ public boolean isAlive = true;
+ public DeathRecipient mRecipient;
+
+ @Override
+ public String getInterfaceDescriptor() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public boolean pingBinder() {
+ return false;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return isAlive;
+ }
+
+ @Override
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+
+ }
+
+ @Override
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+
+ }
+
+ @Override
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public boolean transact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
+ // In any situation, a single binder object should only have at most one death
+ // recipient.
+ assertThat(mRecipient).isNull();
+
+ if (!isAlive) {
+ throw new DeadObjectException();
+ }
+
+ mRecipient = recipient;
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ if (!isAlive) {
+ return false;
+ }
+ assertThat(mRecipient).isSameAs(recipient);
+ mRecipient = null;
+ return true;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ public void die() {
+ isAlive = false;
+ if (mRecipient != null) {
+ mRecipient.binderDied();
+ }
+ mRecipient = null;
+ }
+
+ public boolean hasDeathRecipient() {
+ return mRecipient != null;
+ }
+ }
+
+ @Test
+ public void testRegisterAndUnregister() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+ MyTarget t2 = new MyTarget();
+ MyTarget t3 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+ DeathRecipient r3 = mock(DeathRecipient.class);
+ DeathRecipient r4 = mock(DeathRecipient.class);
+ DeathRecipient r5 = mock(DeathRecipient.class);
+
+ // Start hooking up.
+
+ // Link 3 recipients to t1 -- only one real recipient will be set.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+ assertThat(d.linkToDeath(t1, r3)).isEqualTo(3);
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ // Unlink two -- the real recipient is still set.
+ d.unlinkToDeath(t1, r1);
+ d.unlinkToDeath(t1, r2);
+
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ // Unlink the last one. The real recipient is also unlinked.
+ d.unlinkToDeath(t1, r3);
+ assertThat(t1.hasDeathRecipient()).isFalse();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+ // Set recipients to t1, t2 and t3. t3 has two.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t2, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t3, r1)).isEqualTo(1);
+ assertThat(d.linkToDeath(t3, r2)).isEqualTo(2);
+
+
+ // They should all have a real recipient.
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(t2.hasDeathRecipient()).isTrue();
+ assertThat(t3.hasDeathRecipient()).isTrue();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Unlink r1 from t3. t3 still has r2, so it should still have a real recipient.
+ d.unlinkToDeath(t3, r1);
+ assertThat(t1.hasDeathRecipient()).isTrue();
+ assertThat(t2.hasDeathRecipient()).isTrue();
+ assertThat(t3.hasDeathRecipient()).isTrue();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Unlink r2 from t3. Now t3 has no real recipient.
+ d.unlinkToDeath(t3, r2);
+ assertThat(t3.hasDeathRecipient()).isFalse();
+ assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+ }
+
+ @Test
+ public void testRegisterAndKill() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+ MyTarget t2 = new MyTarget();
+ MyTarget t3 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+ DeathRecipient r3 = mock(DeathRecipient.class);
+ DeathRecipient r4 = mock(DeathRecipient.class);
+ DeathRecipient r5 = mock(DeathRecipient.class);
+
+ // Hook them up.
+
+ d.linkToDeath(t1, r1);
+ d.linkToDeath(t1, r2);
+ d.linkToDeath(t1, r3);
+
+ // r4 is linked then unlinked. It shouldn't be notified.
+ d.linkToDeath(t1, r4);
+ d.unlinkToDeath(t1, r4);
+
+ d.linkToDeath(t2, r1);
+
+ d.linkToDeath(t3, r3);
+ d.linkToDeath(t3, r5);
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(3);
+
+ // Kill the targets.
+
+ t1.die();
+ verify(r1, times(1)).binderDied();
+ verify(r2, times(1)).binderDied();
+ verify(r3, times(1)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(0)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(2);
+
+ reset(r1, r2, r3, r4, r5);
+
+ t2.die();
+ verify(r1, times(1)).binderDied();
+ verify(r2, times(0)).binderDied();
+ verify(r3, times(0)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(0)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(1);
+
+ reset(r1, r2, r3, r4, r5);
+
+ t3.die();
+ verify(r1, times(0)).binderDied();
+ verify(r2, times(0)).binderDied();
+ verify(r3, times(1)).binderDied();
+ verify(r4, times(0)).binderDied();
+ verify(r5, times(1)).binderDied();
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+
+ // Try to register to a dead object -> should return -1.
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(-1);
+
+ assertThat(d.getTargetsForTest().size()).isEqualTo(0);
+ }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fe2492..fdf0bc9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -953,8 +953,8 @@
<!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] -->
<string name="notification_tap_again">Tap again to open</string>
- <!-- Shows when people have pressed the unlock icon to explain how to unlock. [CHAR LIMIT=60] -->
- <string name="keyguard_unlock">Swipe up to unlock</string>
+ <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] -->
+ <string name="keyguard_unlock">Swipe up to open</string>
<!-- Text on keyguard screen and in Quick Settings footer indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=60] -->
<string name="do_disclosure_generic">This device is managed by your organization</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5d1f8b1..3c119a6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -937,6 +937,11 @@
== LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
}
+ public boolean userNeedsStrongAuth() {
+ return mStrongAuthTracker.getStrongAuthForUser(getCurrentUser())
+ != LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+ }
+
public boolean needsSlowUnlockTransition() {
return mNeedsSlowUnlockTransition;
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5e19219..b7bb4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -77,8 +77,10 @@
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarTransitions;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
@@ -125,6 +127,7 @@
private Handler mHandler;
private boolean mAssistHintBlocked = false;
private boolean mIsReceivingNavBarColor = false;
+ private boolean mInGesturalMode;
/**
* Converts a set of {@link Rect}s into a {@link Region}
@@ -149,6 +152,28 @@
mHandler.post(this::startOnScreenDecorationsThread);
setupStatusBarPaddingIfNeeded();
putComponent(ScreenDecorations.class, this);
+ mInGesturalMode = QuickStepContract.isGesturalMode(
+ Dependency.get(NavigationModeController.class)
+ .addListener(this::handleNavigationModeChange));
+ }
+
+ @VisibleForTesting
+ void handleNavigationModeChange(int navigationMode) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(() -> handleNavigationModeChange(navigationMode));
+ return;
+ }
+ boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
+ if (mInGesturalMode != inGesturalMode) {
+ mInGesturalMode = inGesturalMode;
+
+ if (mInGesturalMode && mOverlay == null) {
+ setupDecorations();
+ if (mOverlay != null) {
+ updateLayoutParams();
+ }
+ }
+ }
}
private void fade(View view, boolean fadeIn, boolean isLeft) {
@@ -232,6 +257,7 @@
break;
}
}
+ updateWindowVisibilities();
}
/**
@@ -256,11 +282,15 @@
return thread.getThreadHandler();
}
+ private boolean shouldHostHandles() {
+ return mInGesturalMode;
+ }
+
private void startOnScreenDecorationsThread() {
mRotation = RotationUtils.getExactRotation(mContext);
mWindowManager = mContext.getSystemService(WindowManager.class);
updateRoundedCornerRadii();
- if (hasRoundedCorners() || shouldDrawCutout()) {
+ if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
setupDecorations();
}
@@ -565,7 +595,10 @@
boolean visibleForCutout = shouldDrawCutout()
&& overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
boolean visibleForRoundedCorners = hasRoundedCorners();
- overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
+ boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility()
+ == View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility()
+ == View.VISIBLE;
+ overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles
? View.VISIBLE : View.GONE);
}
@@ -688,10 +721,6 @@
setSize(mOverlay.findViewById(R.id.right), sizeTop);
setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
- setSize(mOverlay.findViewById(R.id.assist_hint_left), sizeTop * 2);
- setSize(mOverlay.findViewById(R.id.assist_hint_right), sizeTop * 2);
- setSize(mBottomOverlay.findViewById(R.id.assist_hint_left), sizeBottom * 2);
- setSize(mBottomOverlay.findViewById(R.id.assist_hint_right), sizeBottom * 2);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0fdab01..34cc70c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -130,8 +131,8 @@
KeyguardBouncer.BouncerExpansionCallback expansionCallback) {
return new KeyguardBouncer(context, callback, lockPatternUtils, container,
dismissCallbackRegistry, FalsingManagerFactory.getInstance(context),
- expansionCallback, KeyguardUpdateMonitor.getInstance(context),
- new Handler(Looper.getMainLooper()));
+ expansionCallback, UnlockMethodCache.getInstance(context),
+ KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper()));
}
public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 5717a54..443c4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.graphics.PixelFormat;
@@ -36,6 +38,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.ImageView;
@@ -72,6 +76,7 @@
protected static final int STATE_PENDING_CONFIRMATION = 3;
protected static final int STATE_AUTHENTICATED = 4;
+ private final AccessibilityManager mAccessibilityManager;
private final IBinder mWindowToken = new Binder();
private final Interpolator mLinearOutSlowIn;
private final WindowManager mWindowManager;
@@ -150,6 +155,7 @@
super(context);
mCallback = callback;
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = mContext.getSystemService(WindowManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
@@ -228,6 +234,10 @@
showTryAgainButton(false /* show */);
mCallback.onTryAgainPressed();
});
+
+ // Must set these in order for the back button events to be received.
+ mLayout.setFocusableInTouchMode(true);
+ mLayout.requestFocus();
}
public void onSaveState(Bundle bundle) {
@@ -280,6 +290,7 @@
final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
if (TextUtils.isEmpty(subtitleText)) {
mSubtitleText.setVisibility(View.GONE);
+ announceAccessibilityEvent();
} else {
mSubtitleText.setVisibility(View.VISIBLE);
mSubtitleText.setText(subtitleText);
@@ -289,6 +300,7 @@
mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
if (TextUtils.isEmpty(descriptionText)) {
mDescriptionText.setVisibility(View.GONE);
+ announceAccessibilityEvent();
} else {
mDescriptionText.setVisibility(View.VISIBLE);
mDescriptionText.setText(descriptionText);
@@ -447,12 +459,14 @@
if (newState == STATE_PENDING_CONFIRMATION) {
mHandler.removeMessages(MSG_RESET_MESSAGE);
mErrorText.setVisibility(View.INVISIBLE);
+ announceAccessibilityEvent();
mPositiveButton.setVisibility(View.VISIBLE);
mPositiveButton.setEnabled(true);
} else if (newState == STATE_AUTHENTICATED) {
mPositiveButton.setVisibility(View.GONE);
mNegativeButton.setVisibility(View.GONE);
mErrorText.setVisibility(View.INVISIBLE);
+ announceAccessibilityEvent();
}
if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) {
@@ -471,14 +485,20 @@
public void restoreState(Bundle bundle) {
mRestoredState = bundle;
- mTryAgainButton.setVisibility(bundle.getInt(KEY_TRY_AGAIN_VISIBILITY));
- mPositiveButton.setVisibility(bundle.getInt(KEY_CONFIRM_VISIBILITY));
+ final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
+ mTryAgainButton.setVisibility(tryAgainVisibility);
+ final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
+ mPositiveButton.setVisibility(confirmVisibility);
mState = bundle.getInt(KEY_STATE);
mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
- mErrorText.setVisibility(bundle.getInt(KEY_ERROR_TEXT_VISIBILITY));
+ final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
+ mErrorText.setVisibility(errorTextVisibility);
+ if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
+ || confirmVisibility == View.INVISIBLE) {
+ announceAccessibilityEvent();
+ }
mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
-
if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
BiometricPrompt.HIDE_DIALOG_DELAY);
@@ -501,4 +521,18 @@
lp.token = mWindowToken;
return lp;
}
+
+ // Every time a view becomes invisible we need to announce an accessibility event.
+ // This is due to an issue in the framework, b/132298701 recommended this workaround.
+ protected void announceAccessibilityEvent() {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(CONTENT_CHANGE_TYPE_SUBTREE);
+ mDialog.sendAccessibilityEventUnchecked(event);
+ mDialog.notifySubtreeAccessibilityStateChanged(mDialog, mDialog,
+ CONTENT_CHANGE_TYPE_SUBTREE);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index 8f26f18..91124cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -149,6 +149,7 @@
private final Runnable mErrorToIdleAnimationRunnable = () -> {
updateState(STATE_IDLE);
mErrorText.setVisibility(View.INVISIBLE);
+ announceAccessibilityEvent();
};
public FaceDialogView(Context context,
@@ -188,6 +189,7 @@
mDialog.invalidateOutline();
mSize = newSize;
+ announceAccessibilityEvent();
} else if (mSize == SIZE_SMALL && newSize == SIZE_BIG) {
mSize = SIZE_GROWING;
@@ -294,6 +296,7 @@
mErrorText.setVisibility(View.VISIBLE);
} else {
mErrorText.setVisibility(View.INVISIBLE);
+ announceAccessibilityEvent();
}
}
@@ -368,11 +371,13 @@
mTryAgainButton.setVisibility(View.VISIBLE);
} else {
mTryAgainButton.setVisibility(View.GONE);
+ announceAccessibilityEvent();
}
}
if (show) {
mPositiveButton.setVisibility(View.GONE);
+ announceAccessibilityEvent();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 835ffc9..6579c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -59,13 +59,15 @@
@Inject
public SysuiColorExtractor(Context context, ConfigurationController configurationController) {
- this(context, new Tonal(context), configurationController, true);
+ this(context, new Tonal(context), configurationController, true,
+ context.getSystemService(WallpaperManager.class));
}
@VisibleForTesting
public SysuiColorExtractor(Context context, ExtractionType type,
- ConfigurationController configurationController, boolean registerVisibility) {
- super(context, type, false /* immediately */);
+ ConfigurationController configurationController, boolean registerVisibility,
+ WallpaperManager wallpaperManager) {
+ super(context, type, false /* immediately */, wallpaperManager);
mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
mWpHiddenColors = new GradientColors();
configurationController.addCallback(this);
@@ -91,13 +93,10 @@
}
}
- WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
- if (wallpaperManager != null) {
- // Listen to all users instead of only the current one.
- wallpaperManager.removeOnColorsChangedListener(this);
- wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
- UserHandle.USER_ALL);
- }
+ // Listen to all users instead of only the current one.
+ wallpaperManager.removeOnColorsChangedListener(this);
+ wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
+ UserHandle.USER_ALL);
}
private void updateDefaultGradients(WallpaperColors colors) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 0f6740d..7c6c556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -569,16 +569,21 @@
switch (behavior) {
case BEHAVIOR_ALERTING:
- alert.setSelected(true);
- silence.setSelected(false);
mPriorityDescriptionView.setVisibility(VISIBLE);
mSilentDescriptionView.setVisibility(GONE);
+ post(() -> {
+ alert.setSelected(true);
+ silence.setSelected(false);
+ });
break;
case BEHAVIOR_SILENT:
- alert.setSelected(false);
- silence.setSelected(true);
+
mSilentDescriptionView.setVisibility(VISIBLE);
mPriorityDescriptionView.setVisibility(GONE);
+ post(() -> {
+ alert.setSelected(false);
+ silence.setSelected(true);
+ });
break;
default:
throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 0b994ff..f5d058c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -54,7 +54,7 @@
public class KeyguardBouncer {
private static final String TAG = "KeyguardBouncer";
- static final long BOUNCER_FACE_DELAY = 800;
+ static final long BOUNCER_FACE_DELAY = 1200;
static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
static final float EXPANSION_HIDDEN = 1f;
static final float EXPANSION_VISIBLE = 0f;
@@ -68,6 +68,7 @@
private final Handler mHandler;
private final BouncerExpansionCallback mExpansionCallback;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -95,7 +96,7 @@
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
- BouncerExpansionCallback expansionCallback,
+ BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) {
mContext = context;
mCallback = callback;
@@ -106,6 +107,7 @@
mDismissCallbackRegistry = dismissCallbackRegistry;
mExpansionCallback = expansionCallback;
mHandler = handler;
+ mUnlockMethodCache = unlockMethodCache;
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
}
@@ -168,7 +170,8 @@
// Split up the work over multiple frames.
DejankUtils.removeCallbacks(mResetRunnable);
- if (mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
+ if (mUnlockMethodCache.isUnlockingWithFacePossible() && !needsFullscreenBouncer()
+ && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
DejankUtils.postAfterTraversal(mShowRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 3ec1609..4bc0036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -238,7 +238,19 @@
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
+
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ ButtonDispatcher imeSwitchButton = getImeSwitchButton();
+ if (imeSwitchButton.getVisibility() == VISIBLE) {
+ // If the IME is not up, but the ime switch button is visible, then make sure that
+ // button is touchable
+ int[] loc = new int[2];
+ View buttonView = imeSwitchButton.getCurrentView();
+ buttonView.getLocationInWindow(loc);
+ info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(),
+ loc[1] + buttonView.getHeight());
+ return;
+ }
info.touchableRegion.setEmpty();
};
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 9536be3..8ad25bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1523,6 +1523,8 @@
@Override // UnlockMethodCache.OnUnlockMethodChangedListener
public void onUnlockMethodStateChanged() {
+ // Unlock method state changed. Notify KeguardMonitor
+ updateKeyguardState();
logStateToEventlog();
}
@@ -3419,9 +3421,7 @@
checkBarModes();
updateScrimController();
mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
- mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mUnlockMethodCache.isMethodSecure(),
- mStatusBarKeyguardViewManager.isOccluded());
+ updateKeyguardState();
Trace.endSection();
}
@@ -3460,6 +3460,12 @@
mStatusBarStateController.setIsDozing(dozing);
}
+ private void updateKeyguardState() {
+ mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
+ mUnlockMethodCache.isMethodSecure(),
+ mStatusBarKeyguardViewManager.isOccluded());
+ }
+
public void onActivationReset() {
mKeyguardIndicationController.hideTransientIndication();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 64ab060..3b5e12c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -45,6 +45,7 @@
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -52,6 +53,7 @@
import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.tuner.TunablePadding;
@@ -78,6 +80,7 @@
private TunerService mTunerService;
private StatusBarWindowView mView;
private TunablePaddingService mTunablePaddingService;
+ private NavigationModeController mNavigationModeController;
@Before
public void setup() {
@@ -87,6 +90,8 @@
mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
mFragmentService = mDependency.injectMockDependency(FragmentService.class);
+ mNavigationModeController = mDependency.injectMockDependency(
+ NavigationModeController.class);
mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
@@ -208,6 +213,54 @@
}
@Test
+ public void testAssistHandles() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+ when(mNavigationModeController.addListener(any())).thenReturn(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+ mScreenDecorations.start();
+
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
+ public void testDelayedAssistHandles() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
+ when(mNavigationModeController.addListener(any())).thenReturn(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
+
+ mScreenDecorations.start();
+
+ // No handles and no corners
+ verify(mWindowManager, never()).addView(any(), any());
+
+ mScreenDecorations.handleNavigationModeChange(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
+
+ // Add 2 windows for rounded corners (top and bottom).
+ verify(mWindowManager, times(2)).addView(any(), any());
+ }
+
+ @Test
public void hasRoundedCornerOverlayFlagSet() {
assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 9c2c822..e3a162c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -40,6 +40,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
@@ -57,6 +58,8 @@
ColorExtractor.TYPE_DARK,
ColorExtractor.TYPE_EXTRA_DARK};
+ @Mock
+ private WallpaperManager mWallpaperManager;
private ColorExtractor.GradientColors mColors;
private SysuiColorExtractor mColorExtractor;
@@ -72,7 +75,7 @@
outGradientColorsNormal.set(mColors);
outGradientColorsDark.set(mColors);
outGradientColorsExtraDark.set(mColors);
- }, mock(ConfigurationController.class), false);
+ }, mock(ConfigurationController.class), false, mWallpaperManager);
}
@Test
@@ -127,7 +130,7 @@
Tonal tonal = mock(Tonal.class);
ConfigurationController configurationController = mock(ConfigurationController.class);
SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(),
- tonal, configurationController, false /* registerVisibility */);
+ tonal, configurationController, false /* registerVisibility */, mWallpaperManager);
verify(configurationController).addCallback(eq(sysuiColorExtractor));
reset(tonal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 3bc5f3e..4e0ef56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -82,6 +82,8 @@
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
+ private UnlockMethodCache mUnlockMethodCache;
+ @Mock
private Handler mHandler;
private KeyguardBouncer mBouncer;
@@ -96,7 +98,7 @@
when(mKeyguardHostView.getHeight()).thenReturn(500);
mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
- mExpansionCallback, mKeyguardUpdateMonitor, mHandler) {
+ mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) {
@Override
protected void inflateView() {
super.inflateView();
@@ -377,7 +379,7 @@
@Test
public void testShow_delaysIfFaceAuthIsRunning() {
- when(mKeyguardUpdateMonitor.isFaceDetectionRunning()).thenReturn(true);
+ when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true);
mBouncer.show(true /* reset */);
ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 7709311..593478c 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -98,7 +98,7 @@
RemoteContentSuggestionsService service = getRemoteServiceLocked();
if (service != null) {
ActivityManager.TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
+ mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
GraphicBuffer snapshotBuffer = null;
int colorSpaceId = 0;
if (snapshot != null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7a2aa7e..a6ff37e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3633,20 +3633,37 @@
mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, true);
}
+ private boolean shouldPromptUnvalidated(NetworkAgentInfo nai) {
+ // Don't prompt if the network is validated, and don't prompt on captive portals
+ // because we're already prompting the user to sign in.
+ if (nai.everValidated || nai.everCaptivePortalDetected) {
+ return false;
+ }
+
+ // If a network has partial connectivity, always prompt unless the user has already accepted
+ // partial connectivity and selected don't ask again. This ensures that if the device
+ // automatically connects to a network that has partial Internet access, the user will
+ // always be able to use it, either because they've already chosen "don't ask again" or
+ // because we have prompt them.
+ if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+ return true;
+ }
+
+ // If a network has no Internet access, only prompt if the network was explicitly selected
+ // and if the user has not already told us to use the network regardless of whether it
+ // validated or not.
+ if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+ return true;
+ }
+
+ return false;
+ }
+
private void handlePromptUnvalidated(Network network) {
if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- // Only prompt if the network is unvalidated or network has partial internet connectivity
- // and was explicitly selected by the user, and if we haven't already been told to switch
- // to it regardless of whether it validated or not. Also don't prompt on captive portals
- // because we're already prompting the user to sign in.
- if (nai == null || nai.everValidated || nai.everCaptivePortalDetected
- || !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated
- // TODO: Once the value of acceptPartialConnectivity is moved to IpMemoryStore,
- // we should reevaluate how to handle acceptPartialConnectivity when network just
- // connected.
- || nai.networkMisc.acceptPartialConnectivity) {
+ if (nai == null || !shouldPromptUnvalidated(nai)) {
return;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 998ee1e..7824a0a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -22,6 +22,7 @@
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
@@ -56,6 +57,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -63,6 +65,7 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderDeathDispatcher;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -83,6 +86,9 @@
static final String TAG = "ContentService";
static final boolean DEBUG = false;
+ /** Do a WTF if a single observer is registered more than this times. */
+ private static final int TOO_MANY_OBSERVERS_THRESHOLD = 1000;
+
public static class Lifecycle extends SystemService {
private ContentService mService;
@@ -135,6 +141,12 @@
private SyncManager mSyncManager = null;
private final Object mSyncManagerLock = new Object();
+ private static final BinderDeathDispatcher<IContentObserver> sObserverDeathDispatcher =
+ new BinderDeathDispatcher<>();
+
+ @GuardedBy("sObserverLeakDetectedUid")
+ private static final ArraySet<Integer> sObserverLeakDetectedUid = new ArraySet<>(0);
+
/**
* Map from userId to providerPackageName to [clientPackageName, uri] to
* value. This structure is carefully optimized to keep invalidation logic
@@ -236,6 +248,13 @@
pw.println();
pw.print(" Total number of nodes: "); pw.println(counts[0]);
pw.print(" Total number of observers: "); pw.println(counts[1]);
+
+ sObserverDeathDispatcher.dump(pw, " ");
+ }
+ synchronized (sObserverLeakDetectedUid) {
+ pw.println();
+ pw.print("Observer leaking UIDs: ");
+ pw.println(sObserverLeakDetectedUid.toString());
}
synchronized (mCache) {
@@ -1345,18 +1364,40 @@
private final Object observersLock;
public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
- int _uid, int _pid, int _userHandle) {
+ int _uid, int _pid, int _userHandle, Uri uri) {
this.observersLock = observersLock;
observer = o;
uid = _uid;
pid = _pid;
userHandle = _userHandle;
notifyForDescendants = n;
- try {
- observer.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
+
+ final int entries = sObserverDeathDispatcher.linkToDeath(observer, this);
+ if (entries == -1) {
binderDied();
+ } else if (entries == TOO_MANY_OBSERVERS_THRESHOLD) {
+ boolean alreadyDetected;
+
+ synchronized (sObserverLeakDetectedUid) {
+ alreadyDetected = sObserverLeakDetectedUid.contains(uid);
+ if (!alreadyDetected) {
+ sObserverLeakDetectedUid.add(uid);
+ }
+ }
+ if (!alreadyDetected) {
+ String caller = null;
+ try {
+ caller = ArrayUtils.firstOrNull(AppGlobals.getPackageManager()
+ .getPackagesForUid(uid));
+ } catch (RemoteException ignore) {
+ }
+ Slog.wtf(TAG, "Observer registered too many times. Leak? cpid=" + pid
+ + " cuid=" + uid
+ + " cpkg=" + caller
+ + " url=" + uri);
+ }
}
+
}
@Override
@@ -1454,7 +1495,7 @@
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
- uid, pid, userHandle));
+ uid, pid, userHandle, uri));
return;
}
@@ -1498,7 +1539,7 @@
if (entry.observer.asBinder() == observerBinder) {
mObservers.remove(i);
// We no longer need to listen for death notifications. Remove it.
- observerBinder.unlinkToDeath(entry, 0);
+ sObserverDeathDispatcher.unlinkToDeath(observer, entry);
break;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bd88594..0032e9a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -617,7 +617,9 @@
mStagingManager.createSession(session);
}
- mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+ }
writeSessionsAsync();
return sessionId;
}
@@ -1210,16 +1212,25 @@
class InternalCallback {
public void onSessionBadgingChanged(PackageInstallerSession session) {
- mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ }
+
writeSessionsAsync();
}
public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
- mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
+ active);
+ }
}
public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
- mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
+ progress);
+ }
}
public void onStagedSessionChanged(PackageInstallerSession session) {
@@ -1232,7 +1243,9 @@
}
public void onSessionFinished(final PackageInstallerSession session, boolean success) {
- mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+ mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+ }
mInstallHandler.post(new Runnable() {
@Override
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 301f650..e107c9a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1079,7 +1079,7 @@
if (info.getPackageName().equals(packageName)) {
mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
saveRollbackData(data);
- return;
+ break;
}
}
}
@@ -1091,7 +1091,6 @@
mAppDataRollbackHelper.snapshotAppData(rollback.data.info.getRollbackId(),
info);
saveRollbackData(rollback.data);
- return;
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index b3b6efe..4f40dc4 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2383,7 +2383,11 @@
r.setVisible(true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
+ // We should not resume activities that being launched behind because these
+ // activities are actually behind other fullscreen activities, but still required
+ // to be visible (such as performing Recents animation).
+ mStackSupervisor.startSpecificActivityLocked(r, andResume && !r.mLaunchTaskBehind,
+ true /* checkConfig */);
return true;
}
}
@@ -2636,7 +2640,7 @@
if (!hasRunningActivity) {
// There are no activities left in the stack, let's look somewhere else.
- return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
+ return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
}
next.delayedResume = false;
@@ -3037,21 +3041,33 @@
return true;
}
- private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
- ActivityOptions options, String reason) {
- final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
- if (nextFocusedStack != null) {
- // Try to move focus to the next visible stack with a running activity if this
- // stack is not covering the entire screen or is on a secondary display (with no home
- // stack).
- return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
- null /* targetOptions */);
+ /**
+ * Resume the next eligible activity in a focusable stack when this one does not have any
+ * running activities left. The focus will be adjusted to the next focusable stack and
+ * top running activities will be resumed in all focusable stacks. However, if the current stack
+ * is a home stack - we have to keep it focused, start and resume a home activity on the current
+ * display instead to make sure that the display is not empty.
+ */
+ private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
+ ActivityOptions options) {
+ final String reason = "noMoreActivities";
+
+ if (!isActivityTypeHome()) {
+ final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+ if (nextFocusedStack != null) {
+ // Try to move focus to the next visible stack with a running activity if this
+ // stack is not covering the entire screen or is on a secondary display with no home
+ // stack.
+ return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
+ prev, null /* targetOptions */);
+ }
}
- // Let's just start up the Launcher...
+ // If the current stack is a home stack, or if focus didn't switch to a different stack -
+ // just start up the Launcher...
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
- "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
+ "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index dbc530d..ab35652 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -559,7 +559,7 @@
/**
* Gets bitmap snapshot of the provided task id.
*/
- public abstract ActivityManager.TaskSnapshot getTaskSnapshot(int taskId,
+ public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
boolean reducedResolution);
/** Returns true if uid is considered foreground for activity start purposes. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8ba7ca1..9fc278e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4521,22 +4521,27 @@
enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
- final TaskRecord task;
- synchronized (mGlobalLock) {
- task = mRootActivityContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
- if (task == null) {
- Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
- return null;
- }
- }
- // Don't call this while holding the lock as this operation might hit the disk.
- return task.getSnapshot(reducedResolution);
+ return getTaskSnapshot(taskId, reducedResolution, true /* restoreFromDisk */);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ private ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution,
+ boolean restoreFromDisk) {
+ final TaskRecord task;
+ synchronized (mGlobalLock) {
+ task = mRootActivityContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+ if (task == null) {
+ Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+ return null;
+ }
+ }
+ // Don't call this while holding the lock as this operation might hit the disk.
+ return task.getSnapshot(reducedResolution, restoreFromDisk);
+ }
+
@Override
public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
synchronized (mGlobalLock) {
@@ -7419,10 +7424,10 @@
}
@Override
- public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
- synchronized (mGlobalLock) {
- return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution);
- }
+ public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
+ boolean reducedResolution) {
+ return ActivityTaskManagerService.this.getTaskSnapshot(taskId, reducedResolution,
+ false /* restoreFromDisk */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index cae7612..4a9a3f7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -176,6 +176,7 @@
boolean inPendingTransaction;
boolean allDrawn;
private boolean mLastAllDrawn;
+ private boolean mUseTransferredAnimation;
// Set to true when this app creates a surface while in the middle of an animation. In that
// case do not clear allDrawn until the animation completes.
@@ -618,9 +619,12 @@
boolean runningAppAnimation = false;
if (transit != WindowManager.TRANSIT_UNSET) {
- if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
- delayed = runningAppAnimation = true;
+ if (mUseTransferredAnimation) {
+ runningAppAnimation = isReallyAnimating();
+ } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
+ runningAppAnimation = true;
}
+ delayed = runningAppAnimation;
final WindowState window = findMainWindow();
if (window != null && accessibilityController != null) {
accessibilityController.onAppWindowTransitionLocked(window, transit);
@@ -667,6 +671,7 @@
getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
}
}
+ mUseTransferredAnimation = false;
if (isReallyAnimating()) {
delayed = true;
@@ -1531,9 +1536,9 @@
transferAnimation(fromToken);
// When transferring an animation, we no longer need to apply an animation to the
- // the token we transfer the animation over. Thus, remove the animation from
- // pending opening apps.
- getDisplayContent().mOpeningApps.remove(this);
+ // the token we transfer the animation over. Thus, set this flag to indicate we've
+ // transferred the animation.
+ mUseTransferredAnimation = true;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 298b302..3fd4e83 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -851,11 +851,12 @@
/**
* DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
*/
- TaskSnapshot getSnapshot(boolean reducedResolution) {
+ TaskSnapshot getSnapshot(boolean reducedResolution, boolean restoreFromDisk) {
// TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
// synchronized between AM and WM.
- return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
+ return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution,
+ restoreFromDisk);
}
void touchActiveTime() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0a2fc9b..45c7e69 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3551,8 +3551,9 @@
return true;
}
- public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {
- return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,
+ public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution,
+ boolean restoreFromDisk) {
+ return mTaskSnapshotController.getSnapshot(taskId, userId, restoreFromDisk,
reducedResolution);
}
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 62b0ca8..891ca74 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -30,7 +30,7 @@
import com.android.server.content.ContentService.ObserverNode;
/**
- * bit FrameworksServicesTests:com.android.server.content.ObserverNodeTest
+ * atest FrameworksServicesTests:com.android.server.content.ObserverNodeTest
*/
@SmallTest
public class ObserverNodeTest extends AndroidTestCase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 8f41a42..1f8b33e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -24,14 +24,18 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
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.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -107,6 +111,39 @@
}
@Test
+ public void testRestartRecentsActivity() throws Exception {
+ // Have a recents activity that is not attached to its process (ActivityRecord.app = null).
+ ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ ActivityStack recentsStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ ActivityRecord recentActivity = new ActivityBuilder(mService).setComponent(
+ mRecentsComponent).setCreateTask(true).setStack(recentsStack).build();
+ WindowProcessController app = recentActivity.app;
+ recentActivity.app = null;
+
+ // Start an activity on top.
+ new ActivityBuilder(mService).setCreateTask(true).build().getActivityStack().moveToFront(
+ "testRestartRecentsActivity");
+
+ doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
+ any() /* starting */, anyInt() /* configChanges */,
+ anyBoolean() /* preserveWindows */);
+ doReturn(app).when(mService).getProcessController(eq(recentActivity.processName), anyInt());
+ ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
+ doNothing().when(lifecycleManager).scheduleTransaction(any());
+ AppWarnings appWarnings = mService.getAppWarningsLocked();
+ spyOn(appWarnings);
+ doNothing().when(appWarnings).onStartActivity(any());
+
+ startRecentsActivity();
+
+ // Recents activity must be restarted, but not be resumed while running recents animation.
+ verify(mRootActivityContainer.mStackSupervisor).startSpecificActivityLocked(
+ eq(recentActivity), eq(false), anyBoolean());
+ assertThat(recentActivity.getState()).isEqualTo(PAUSED);
+ }
+
+ @Test
public void testSetLaunchTaskBehindOfTargetActivity() {
ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
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 c77e25f..8d2c3dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -35,6 +36,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static org.junit.Assert.assertEquals;
@@ -397,6 +399,58 @@
}
/**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
+ mFullscreenStack.remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+ mService.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+ }
+
+ /**
+ * Verify that home activity will be started on a display even if another display has a
+ * focusable activity.
+ */
+ @Test
+ public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
+ mFullscreenStack.remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY).getHomeStack().remove();
+ mService.mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY)
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+
+ // Create an activity on secondary display.
+ final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+ ActivityDisplay.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ new ActivityBuilder(mService).setTask(task).build();
+
+ doReturn(true).when(mRootActivityContainer).resumeHomeActivity(any(), any(), anyInt());
+
+ mService.setBooted(true);
+
+ // Trigger resume on all displays
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+
+ // Verify that home activity was started on the default display
+ verify(mRootActivityContainer).resumeHomeActivity(any(), any(), eq(DEFAULT_DISPLAY));
+ }
+
+ /**
* Verify that a lingering transition is being executed in case the activity to be resumed is
* already resumed
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8ef381d..9806199 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2685,6 +2685,30 @@
public static final String KEY_PREFIX = "gps.";
/**
+ * Location information during (and after) an emergency call is only provided over control
+ * plane signaling from the network.
+ * @hide
+ */
+ public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
+
+ /**
+ * Location information during (and after) an emergency call is provided over the data
+ * plane and serviced by the framework GNSS service, but if it fails, the carrier also
+ * supports control plane backup signaling.
+ * @hide
+ */
+ public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
+
+ /**
+ * Location information during (and after) an emergency call is provided over the data plane
+ * and serviced by the framework GNSS service only. There is no backup signalling over the
+ * control plane if it fails.
+ * @hide
+ */
+ public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
+
+
+ /**
* Determine whether current lpp_mode used for E-911 needs to be kept persistently.
* {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
* when sim is not presented.
@@ -2775,6 +2799,23 @@
*/
public static final String KEY_NFW_PROXY_APPS_STRING = KEY_PREFIX + "nfw_proxy_apps";
+ /**
+ * Determines whether or not SUPL ES mode supports a control-plane mechanism to get a user's
+ * location in the event that data plane SUPL fails or is otherwise unavailable.
+ * <p>
+ * An integer value determines the support type of this carrier. If this carrier only
+ * supports data plane SUPL ES, then the value will be
+ * {@link #SUPL_EMERGENCY_MODE_TYPE_DP_ONLY}. If the carrier supports control plane fallback
+ * for emergency SUPL, the value will be {@link #SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK}.
+ * If the carrier does not support data plane SUPL using the framework, the value will be
+ * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+ * <p>
+ * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+ * @hide
+ */
+ public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
+ + "es_supl_control_plane_support_int";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -2789,6 +2830,8 @@
defaults.putString(KEY_GPS_LOCK_STRING, "3");
defaults.putString(KEY_ES_EXTENSION_SEC_STRING, "0");
defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
+ defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d6011b9..b7b511e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1498,6 +1498,48 @@
*/
public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
+ /**
+ * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+ * to indicate if the SIM combination in DSDS has limitation or compatible issue.
+ * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
+ *
+ * @hide
+ */
+ public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
+ "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
+
+ /** @hide */
+ @IntDef({
+ EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
+ EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SimCombinationWarningType{}
+
+ /**
+ * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+ * to indicate there's no SIM combination warning.
+ * @hide
+ */
+ public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
+
+ /**
+ * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+ * to indicate two active SIMs are both CDMA hence there might be functional limitation.
+ * @hide
+ */
+ public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
+
+ /**
+ * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+ * to indicate what's the name of SIM combination it has limitation or compatible issue.
+ * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
+ * name will be "operator1 & operator2".
+ *
+ * @hide
+ */
+ public static final String EXTRA_SIM_COMBINATION_NAMES =
+ "android.telephony.extra.SIM_COMBINATION_NAMES";
//
//
// Device Info
diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
index 17fa931..f88a7c4 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
@@ -47,17 +47,19 @@
@RunWith(AndroidJUnit4.class)
public class ColorExtractorTest {
- Context mContext;
+ private Context mContext;
+ private WallpaperManager mWallpaperManager;
@Before
public void setup() {
mContext = InstrumentationRegistry.getContext();
+ mWallpaperManager = mock(WallpaperManager.class);
}
@Test
public void ColorExtractor_extractWhenInitialized() {
ExtractionType type = mock(Tonal.class);
- new ColorExtractor(mContext, type, true);
+ new ColorExtractor(mContext, type, true, mWallpaperManager);
// 1 for lock and 1 for system
verify(type, times(2))
.extractInto(any(), any(), any(), any());
@@ -84,7 +86,7 @@
outGradientColorsDark.set(colorsExpectedDark);
outGradientColorsExtraDark.set(colorsExpectedExtraDark);
};
- ColorExtractor extractor = new ColorExtractor(mContext, type, true);
+ ColorExtractor extractor = new ColorExtractor(mContext, type, true, mWallpaperManager);
GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM,
ColorExtractor.TYPE_NORMAL);
@@ -99,7 +101,8 @@
public void addOnColorsChangedListener_invokesListener() {
ColorExtractor.OnColorsChangedListener mockedListeners =
mock(ColorExtractor.OnColorsChangedListener.class);
- ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true);
+ ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true,
+ mWallpaperManager);
extractor.addOnColorsChangedListener(mockedListeners);
extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.RED), null, null),