Merge "Add a platform-compat-config to the framework services." am: 6162efbfa7
am: 8ee5669a5e
Change-Id: Ie340605827027e34d8a13dc0de19bcc6950b8d36
diff --git a/api/test-current.txt b/api/test-current.txt
index d566027..e8e27cb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -54,6 +54,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method public long getTotalRam();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
+ method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index cebe6e12..90b80e7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -309,7 +309,7 @@
* Name under which an AccessibilityService component publishes information
* about itself. This meta-data must reference an XML resource containing an
* <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>
- * tag. This is a a sample XML file configuring an accessibility service:
+ * tag. This is a sample XML file configuring an accessibility service:
* <pre> <accessibility-service
* android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
* android:packageNames="foo.bar, foo.baz"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index dc52c52..f5b0b59 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1547,7 +1547,9 @@
* had previously been frozen by {@link #onSaveInstanceState}.
*
* <p>This method is called between {@link #onStart} and
- * {@link #onPostCreate}.
+ * {@link #onPostCreate}. This method is called only when recreating
+ * an activity; the method isn't invoked if {@link #onStart} is called for
+ * any other reason.</p>
*
* @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
*
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 17368b7..91b98c7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -925,7 +925,7 @@
* (which tends to consume a lot more RAM).
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
static public boolean isHighEndGfx() {
return !isLowRamDeviceStatic()
&& !RoSystemProperties.CONFIG_AVOID_GFX_ACCEL
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d780b09..1b51573 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1005,8 +1005,7 @@
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
- ContentCaptureOptions contentCaptureOptions,
- long[] disabledCompatChanges) {
+ ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
if (services != null) {
if (false) {
// Test code to make sure the app could see the passed-in services.
@@ -1052,6 +1051,8 @@
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
+ data.autofillOptions = autofillOptions;
+ data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
sendMessage(H.BIND_APPLICATION, data);
}
@@ -6133,7 +6134,6 @@
if (data.trackAllocation) {
DdmVmInternal.enableRecentAllocations(true);
}
-
// Note when this process has started.
Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2f0782b..fb72e65 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -240,7 +240,8 @@
public @interface UidState {}
/**
- * Uid state: The UID is a foreground persistent app.
+ * Uid state: The UID is a foreground persistent app. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -248,7 +249,8 @@
public static final int UID_STATE_PERSISTENT = 100;
/**
- * Uid state: The UID is top foreground app.
+ * Uid state: The UID is top foreground app. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -257,6 +259,7 @@
/**
* Uid state: The UID is running a foreground service of location type.
+ * The lower the UID state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -264,7 +267,8 @@
public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300;
/**
- * Uid state: The UID is running a foreground service.
+ * Uid state: The UID is running a foreground service. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -279,7 +283,8 @@
public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE;
/**
- * Uid state: The UID is a foreground app.
+ * Uid state: The UID is a foreground app. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -287,7 +292,8 @@
public static final int UID_STATE_FOREGROUND = 500;
/**
- * Uid state: The UID is a background app.
+ * Uid state: The UID is a background app. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
@@ -295,7 +301,8 @@
public static final int UID_STATE_BACKGROUND = 600;
/**
- * Uid state: The UID is a cached app.
+ * Uid state: The UID is a cached app. The lower the UID
+ * state the more important the UID is for the user.
* @hide
*/
@TestApi
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 41733b3..9720e9f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -50,6 +50,7 @@
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowManagerGlobal;
@@ -528,6 +529,12 @@
} while (mWaitingActivities.contains(aw));
waitForEnterAnimationComplete(aw.activity);
+
+ // Apply an empty transaction to ensure SF has a chance to update before
+ // the Activity is ready (b/138263890).
+ try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+ t.apply(true);
+ }
return aw.activity;
}
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 099ae29..e78fb7f 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -339,6 +339,8 @@
* for {@link #TYPE_STEP_COUNTER} instead. It is defined as a
* {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER} sensor.
* <p>
+ * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+ * <p>
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
public static final int TYPE_STEP_DETECTOR = 18;
@@ -384,8 +386,6 @@
* gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't
* use the gyroscope. However, it is more noisy and will work best outdoors.
* <p>
- * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
- * <p>
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
*/
public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
index 4e5eaac..a1ed214 100644
--- a/core/java/android/os/RedactingFileDescriptor.java
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -35,7 +35,7 @@
/**
* Variant of {@link FileDescriptor} that allows its creator to specify regions
- * that should be redacted (appearing as zeros to the reader).
+ * that should be redacted.
*
* @hide
*/
@@ -44,13 +44,16 @@
private static final boolean DEBUG = true;
private volatile long[] mRedactRanges;
+ private volatile long[] mFreeOffsets;
private FileDescriptor mInner = null;
private ParcelFileDescriptor mOuter = null;
- private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges)
+ private RedactingFileDescriptor(
+ Context context, File file, int mode, long[] redactRanges, long[] freeOffsets)
throws IOException {
mRedactRanges = checkRangesArgument(redactRanges);
+ mFreeOffsets = freeOffsets;
try {
try {
@@ -88,13 +91,17 @@
*
* @param file The underlying file to open.
* @param mode The {@link ParcelFileDescriptor} mode to open with.
- * @param redactRanges List of file offsets that should be redacted, stored
+ * @param redactRanges List of file ranges that should be redacted, stored
* as {@code [start1, end1, start2, end2, ...]}. Start values are
* inclusive and end values are exclusive.
+ * @param freePositions List of file offsets at which the four byte value 'free' should be
+ * written instead of zeros within parts of the file covered by {@code redactRanges}.
+ * Non-redacted bytes will not be modified even if covered by a 'free'. This is
+ * useful for overwriting boxes in ISOBMFF files with padding data.
*/
public static ParcelFileDescriptor open(Context context, File file, int mode,
- long[] redactRanges) throws IOException {
- return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter;
+ long[] redactRanges, long[] freePositions) throws IOException {
+ return new RedactingFileDescriptor(context, file, mode, redactRanges, freePositions).mOuter;
}
/**
@@ -169,6 +176,15 @@
for (long j = start; j < end; j++) {
data[(int) (j - offset)] = 0;
}
+ // Overwrite data at 'free' offsets within the redaction ranges.
+ for (long freeOffset : mFreeOffsets) {
+ final long freeEnd = freeOffset + 4;
+ final long redactFreeStart = Math.max(freeOffset, start);
+ final long redactFreeEnd = Math.min(freeEnd, end);
+ for (long j = redactFreeStart; j < redactFreeEnd; j++) {
+ data[(int) (j - offset)] = (byte) "free".charAt((int) (j - freeOffset));
+ }
+ }
}
return n;
}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 7df4b5d..57a8801 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -25,8 +25,6 @@
import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
-
import java.io.Closeable;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
@@ -64,7 +62,7 @@
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
+ new Closer(mFileDescriptor, mMemoryRegistration));
}
/**
@@ -261,9 +259,6 @@
mCleaner.clean();
mCleaner = null;
}
-
- // Cleaner.clean doesn't clear the value of the file descriptor.
- mFileDescriptor.setInt$(-1);
}
@Override
@@ -295,24 +290,19 @@
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
- // This is a copy of the FileDescriptor we're attached to, in order to avoid a reference
- // cycle.
private FileDescriptor mFd;
private MemoryRegistration mMemoryReference;
- private Closer(int fd, MemoryRegistration memoryReference) {
- mFd = new FileDescriptor();
- mFd.setInt$(fd);
- IoUtils.setFdOwner(mFd, this);
-
+ private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
+ mFd = fd;
mMemoryReference = memoryReference;
}
@Override
public void run() {
- IoUtils.closeQuietly(mFd);
- mFd = null;
-
+ try {
+ Os.close(mFd);
+ } catch (ErrnoException e) { /* swallow error */ }
mMemoryReference.release();
mMemoryReference = null;
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 81e1eb9..af3a16c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -870,8 +870,8 @@
protected interface ContactOptionsColumns {
/**
* The number of times a contact has been contacted.
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. For
- * more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.</p>
* <P>Type: INTEGER</P>
@@ -885,8 +885,8 @@
/**
* The last time a contact was contacted.
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete. For
- * more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.</p>
* <P>Type: INTEGER</P>
@@ -1691,10 +1691,10 @@
* TIMES_CONTACTED field is incremented by 1 and the LAST_TIME_CONTACTED
* field is populated with the current system time.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this method is obsolete. For
- * more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
- * page.
+ * page.</p>
*
* @param resolver the ContentResolver to use
* @param contactId the person who was contacted
@@ -1730,8 +1730,8 @@
* Frequent contacts are no longer included in the result as of
* Android version {@link android.os.Build.VERSION_CODES#Q}.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts
- * results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this
+ * field doesn't sort results based on contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -1745,8 +1745,8 @@
* Android version {@link android.os.Build.VERSION_CODES#Q}.
* This URI always returns an empty cursor.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts
- * results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this
+ * field doesn't sort results based on contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -1760,8 +1760,8 @@
* various parts of the contact name. The filter argument should be passed
* as an additional path segment after this URI.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts
- * results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this
+ * field doesn't sort results based on contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -4292,10 +4292,10 @@
* Android version {@link android.os.Build.VERSION_CODES#Q}.
* This column always contains 0.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete.
- * For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
- * page.
+ * page.</p>
*/
@Deprecated
public static final String LAST_TIME_USED = "last_time_used";
@@ -4306,10 +4306,10 @@
* Android version {@link android.os.Build.VERSION_CODES#Q}.
* This column always contains 0.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field is obsolete.
- * For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
- * page.
+ * page.</p>
*/
@Deprecated
public static final String TIMES_USED = "times_used";
@@ -5259,8 +5259,8 @@
/**
* The content:// style URI for this table.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this
+ * field doesn't sort results based on contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*
@@ -5277,8 +5277,8 @@
/**
* <p>URI used for the "enterprise caller-id".</p>
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store, this
+ * field doesn't sort results based on contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*
@@ -6079,8 +6079,8 @@
* to display names as well as phone numbers. The filter argument should be passed
* as an additional path segment after this URI.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>This field deosn't sort results based on contacts
+ * frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -6092,8 +6092,9 @@
* same columns. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in
* parameters, otherwise it will throw IllegalArgumentException.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts
- * results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field doesn't sort results based on contacts frequency. For more information,
+ * see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -6360,8 +6361,9 @@
* as an additional path segment after this URI.
* </p>
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer sorts
- * results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field doesn't sort results based on contacts frequency. For more information,
+ * see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.</p>
*
@@ -6383,8 +6385,9 @@
* same columns. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in
* parameters, otherwise it will throw IllegalArgumentException.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field doesn't sort results based on contacts frequency. For more information,
+ * see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -7602,8 +7605,8 @@
* <p>Similar to {@link Phone#CONTENT_FILTER_URI}, but allows users to filter callable
* data.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>This field no longer sorts results based on
+ * contacts frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -7615,8 +7618,9 @@
* callable data. This URI requires {@link ContactsContract#DIRECTORY_PARAM_KEY} in
* parameters, otherwise it will throw IllegalArgumentException.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field doesn't sort results based on contacts frequency. For more information,
+ * see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.</p>
*/
@@ -7646,8 +7650,9 @@
* <p>The content:// style URI for these data items, which allows for a query parameter
* to be appended onto the end to filter for data items matching the query.
*
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this field no longer
- * sorts results based on contacts frequency. For more information, see the
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field doesn't sort results based on contacts frequency. For more information,
+ * see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
*/
@@ -8298,15 +8303,14 @@
}
/**
- * <p class="caution"><b>Caution: </b>As of January 7, 2019, this class is obsolete. For
- * more information, see the
- * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
- * page.
- * </p>
* <p>
* API allowing applications to send usage information for each {@link Data} row to the
* Contacts Provider. Applications can also clear all usage information.
* </p>
+ * <p class="caution"><b>Caution: </b>If you publish your app to the Google Play Store,
+ * this field is obsolete, regardless of Android version. For more information, see the
+ * <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
+ * page.</p>
* <p>
* With the feedback, Contacts Provider may return more contextually appropriate results for
* Data listing, typically supplied with
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index caab00c..775ec99 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -300,5 +300,5 @@
/**
* Called when the system gesture exclusion has changed.
*/
- void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
+ oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf6191e..e3db475 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -967,6 +967,19 @@
*/
static boolean sBrokenInsetsDispatch;
+ /**
+ * Prior to Q, calling
+ * {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)}
+ * did not call update the window format so the opacity of the background was not correctly
+ * applied to the window. Some applications rely on this misbehavior to work properly.
+ * <p>
+ * From Q, {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} is
+ * the same as {@link com.android.internal.policy.DecorView#setWindowBackground(Drawable)}
+ * which updates the window format.
+ * @hide
+ */
+ protected static boolean sBrokenWindowBackground;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -5104,6 +5117,8 @@
sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
|| targetSdkVersion < Build.VERSION_CODES.Q;
+ sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
+
sCompatibilityDone = true;
}
}
@@ -10556,6 +10571,13 @@
*
* <p>Do not modify the provided list after this method is called.</p>
*
+ * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the
+ * exclusions it takes into account. The limit does not apply while the navigation
+ * bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
+ * {@link android.inputmethodservice.InputMethodService input method} and
+ * {@link Intent#CATEGORY_HOME home activity}.
+ * </p>
+ *
* @param rects A list of precision gesture regions that this view needs to function correctly
*/
public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9340b71..bcc6a55 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
@@ -644,6 +645,14 @@
* {@link View#setSystemGestureExclusionRects} outside of the
* {@link #getMandatorySystemGestureInsets() mandatory system gesture insets}.
*
+ * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the
+ * exclusions it takes into account. The limit does not apply while the navigation
+ * bar is {@link View#SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
+ * {@link android.inputmethodservice.InputMethodService input method} and
+ * {@link Intent#CATEGORY_HOME home activity}.
+ * </p>
+ *
+ *
* <p>Simple taps are guaranteed to reach the window even within the system gesture insets,
* as long as they are outside the {@link #getTappableElementInsets() system window insets}.
*
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 18ec334..ac145b1 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -32,7 +32,7 @@
* avoid unintentionally granting requests for new permissions, you should pass the
* specific permissions you intend to grant to {@link #grant(String[]) grant()},
* and avoid writing code like this example:
- * <pre>
+ * <pre class="prettyprint">
* permissionRequest.grant(permissionRequest.getResources()) // This is wrong!!!
* </pre>
* See the WebView's release notes for information about new protected resources.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 95fe963..4db6308 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -519,15 +519,17 @@
* may not be supported and applications wishing to support these sources
* or more advanced file operations should build their own Intent.
*
- * <pre>
- * How to use:
- * 1. Build an intent using {@link #createIntent}
- * 2. Fire the intent using {@link android.app.Activity#startActivityForResult}.
- * 3. Check for ActivityNotFoundException and take a user friendly action if thrown.
- * 4. Listen the result using {@link android.app.Activity#onActivityResult}
- * 5. Parse the result using {@link #parseResult} only if media capture was not requested.
- * 6. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser}
- * </pre>
+ * <p>How to use:
+ * <ol>
+ * <li>Build an intent using {@link #createIntent}</li>
+ * <li>Fire the intent using {@link android.app.Activity#startActivityForResult}.</li>
+ * <li>Check for ActivityNotFoundException and take a user friendly action if thrown.</li>
+ * <li>Listen the result using {@link android.app.Activity#onActivityResult}</li>
+ * <li>Parse the result using {@link #parseResult} only if media capture was not
+ * requested.</li>
+ * <li>Send the result using filePathCallback of {@link
+ * WebChromeClient#onShowFileChooser}</li>
+ * </ol>
*
* @return an Intent that supports basic file chooser sources.
*/
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index a46580d..18d4d69 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -464,7 +464,9 @@
* Note that the feature will continue to be supported on older versions of
* Android as before.
*
- * This function does not have any effect.
+ * @deprecated In Android O and afterwards, this function does not have
+ * any effect, the form data will be saved to platform's autofill service
+ * if applicable.
*/
@Deprecated
public abstract void setSaveFormData(boolean save);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 14be73d..aed6c9c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -759,7 +759,7 @@
* encoded. If the data is base64 encoded, the value of the encoding
* parameter must be {@code "base64"}. HTML can be encoded with {@link
* android.util.Base64#encodeToString(byte[],int)} like so:
- * <pre>
+ * <pre class="prettyprint">
* String unencodedHtml =
* "<html><body>'%28' is the code for '('</body></html>";
* String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
@@ -1851,7 +1851,7 @@
* important security note below for implications.
* <p> Note that injected objects will not appear in JavaScript until the page is next
* (re)loaded. JavaScript should be enabled before injecting the object. For example:
- * <pre>
+ * <pre class="prettyprint">
* class JsObject {
* {@literal @}JavascriptInterface
* public String toString() { return "injectedObject"; }
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index c3c2c0d..2bf1ba5 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -50,7 +50,7 @@
* override {@link #getView(int, View, ViewGroup)}
* and inflate a view resource.
* For a code example, see
- * the <a href="https://developer.android.com/samples/CustomChoiceList/index.html">
+ * the <a href="https://github.com/googlesamples/android-CustomChoiceList/#readme">
* CustomChoiceList</a> sample.
* </p>
* <p>
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index d985528..6b324a5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1202,13 +1202,13 @@
* determine where to position the view on the screen. If the view is not contained
* within a relative layout, these attributes are ignored.
*
- * See the <a href="/guide/topics/ui/layout/relative.html">
- * Relative Layout</a> guide for example code demonstrating how to use relative layout’s
+ * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
+ * Layout</a> guide for example code demonstrating how to use relative layout's
* layout parameters in a layout XML.
*
* To learn more about layout parameters and how they differ from typical view attributes,
- * see the <a href="/guide/topics/ui/declaring-layout.html#attributes">
- * Layouts guide</a>.
+ * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts
+ * guide</a>.
*
*
* @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 49a0f39..4c67b08 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -433,7 +433,7 @@
* to the next tabbed view, in this example).
* <p>
* To move both the focus AND the selected tab at once, please use
- * {@link #setCurrentTab}. Normally, the view logic takes care of
+ * {@link #focusCurrentTab}. Normally, the view logic takes care of
* adjusting the focus, so unless you're circumventing the UI,
* you'll probably just focus your interest here.
*
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 5778544..157e0a7 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -16,252 +16,167 @@
package com.android.internal.app;
+import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
import android.view.View;
-import android.widget.FrameLayout;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.internal.R;
import org.json.JSONObject;
+/**
+ * @hide
+ */
public class PlatLogoActivity extends Activity {
- FrameLayout layout;
- TimeAnimator anim;
- PBackground bg;
+ ImageView mZeroView, mOneView;
+ BackslashDrawable mBackslash;
+ int mClicks;
- private class PBackground extends Drawable {
- private float maxRadius, radius, x, y, dp;
- private int[] palette;
- private int darkest;
- private float offset;
+ static final Paint sPaint = new Paint();
+ static {
+ sPaint.setStyle(Paint.Style.STROKE);
+ sPaint.setStrokeWidth(4f);
+ sPaint.setStrokeCap(Paint.Cap.SQUARE);
+ }
- public PBackground() {
- randomizePalette();
+ @Override
+ protected void onPause() {
+ if (mBackslash != null) {
+ mBackslash.stopAnimating();
}
-
- /**
- * set inner radius of "p" logo
- */
- public void setRadius(float r) {
- this.radius = Math.max(48*dp, r);
- }
-
- /**
- * move the "p"
- */
- public void setPosition(float x, float y) {
- this.x = x;
- this.y = y;
- }
-
- /**
- * for animating the "p"
- */
- public void setOffset(float o) {
- this.offset = o;
- }
-
- /**
- * rough luminance calculation
- * https://www.w3.org/TR/AERT/#color-contrast
- */
- public float lum(int rgb) {
- return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f;
- }
-
- /**
- * create a random evenly-spaced color palette
- * guaranteed to contrast!
- */
- public void randomizePalette() {
- final int slots = 2 + (int)(Math.random() * 2);
- float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f };
- palette = new int[slots];
- darkest = 0;
- for (int i=0; i<slots; i++) {
- palette[i] = Color.HSVToColor(color);
- color[0] = (color[0] + 360f/slots) % 360f;
- if (lum(palette[i]) < lum(palette[darkest])) darkest = i;
- }
-
- final StringBuilder str = new StringBuilder();
- for (int c : palette) {
- str.append(String.format("#%08x ", c));
- }
- Log.v("PlatLogoActivity", "color palette: " + str);
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (dp == 0) dp = getResources().getDisplayMetrics().density;
- final float width = canvas.getWidth();
- final float height = canvas.getHeight();
- if (radius == 0) {
- setPosition(width / 2, height / 2);
- setRadius(width / 6);
- }
- final float inner_w = radius * 0.667f;
-
- final Paint paint = new Paint();
- paint.setStrokeCap(Paint.Cap.BUTT);
- canvas.translate(x, y);
-
- Path p = new Path();
- p.moveTo(-radius, height);
- p.lineTo(-radius, 0);
- p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
- p.lineTo(-radius, radius);
-
- float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f;
- paint.setStyle(Paint.Style.FILL);
-
- int i=0;
- while (w > radius*2 + inner_w*2) {
- paint.setColor(0xFF000000 | palette[i % palette.length]);
- // for a slower but more complete version:
- // paint.setStrokeWidth(w);
- // canvas.drawPath(p, paint);
- canvas.drawOval(-w/2, -w/2, w/2, w/2, paint);
- w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f));
- i++;
- }
-
- // the innermost circle needs to be a constant color to avoid rapid flashing
- paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]);
- canvas.drawOval(-radius, -radius, radius, radius, paint);
-
- p.reset();
- p.moveTo(-radius, height);
- p.lineTo(-radius, 0);
- p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
- p.lineTo(-radius + inner_w, radius);
-
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(inner_w*2);
- paint.setColor(palette[darkest]);
- canvas.drawPath(p, paint);
- paint.setStrokeWidth(inner_w);
- paint.setColor(0xFFFFFFFF);
- canvas.drawPath(p, paint);
- }
-
- @Override
- public void setAlpha(int alpha) {
-
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return 0;
- }
+ mClicks = 0;
+ super.onPause();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ final float dp = getResources().getDisplayMetrics().density;
- layout = new FrameLayout(this);
- setContentView(layout);
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ getWindow().setNavigationBarColor(0);
+ getWindow().setStatusBarColor(0);
- bg = new PBackground();
- layout.setBackground(bg);
+ getActionBar().hide();
- final ContentResolver cr = getContentResolver();
+ setContentView(R.layout.platlogo_layout);
- layout.setOnTouchListener(new View.OnTouchListener() {
- final String TOUCH_STATS = "touch.stats";
+ mBackslash = new BackslashDrawable((int) (50 * dp));
- final PointerCoords pc0 = new PointerCoords();
- final PointerCoords pc1 = new PointerCoords();
+ mOneView = findViewById(R.id.one);
+ mOneView.setImageDrawable(new OneDrawable());
+ mZeroView = findViewById(R.id.zero);
+ mZeroView.setImageDrawable(new ZeroDrawable());
- double pressure_min, pressure_max;
- int maxPointers;
- int tapCount;
+ final ViewGroup root = (ViewGroup) mOneView.getParent();
+ root.setClipChildren(false);
+ root.setBackground(mBackslash);
+ root.getBackground().setAlpha(0x20);
+ View.OnTouchListener tl = new View.OnTouchListener() {
+ float mOffsetX, mOffsetY;
+ long mClickTime;
+ ObjectAnimator mRotAnim;
@Override
public boolean onTouch(View v, MotionEvent event) {
- final float pressure = event.getPressure();
+ measureTouchPressure(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- pressure_min = pressure_max = pressure;
- // fall through
- case MotionEvent.ACTION_MOVE:
- if (pressure < pressure_min) pressure_min = pressure;
- if (pressure > pressure_max) pressure_max = pressure;
- final int pc = event.getPointerCount();
- if (pc > maxPointers) maxPointers = pc;
- if (pc > 1) {
- event.getPointerCoords(0, pc0);
- event.getPointerCoords(1, pc1);
- bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f);
+ v.animate().scaleX(1.1f).scaleY(1.1f);
+ v.getParent().bringChildToFront(v);
+ mOffsetX = event.getRawX() - v.getX();
+ mOffsetY = event.getRawY() - v.getY();
+ long now = System.currentTimeMillis();
+ if (now - mClickTime < 350) {
+ mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION,
+ v.getRotation(), v.getRotation() + 3600);
+ mRotAnim.setDuration(10000);
+ mRotAnim.start();
+ mClickTime = 0;
+ } else {
+ mClickTime = now;
}
break;
- case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_MOVE:
+ v.setX(event.getRawX() - mOffsetX);
+ v.setY(event.getRawY() - mOffsetY);
+ v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
+ break;
case MotionEvent.ACTION_UP:
- try {
- final String touchDataJson = Settings.System.getString(cr, TOUCH_STATS);
- final JSONObject touchData = new JSONObject(
- touchDataJson != null ? touchDataJson : "{}");
- if (touchData.has("min")) {
- pressure_min = Math.min(pressure_min, touchData.getDouble("min"));
- }
- if (touchData.has("max")) {
- pressure_max = Math.max(pressure_max, touchData.getDouble("max"));
- }
- touchData.put("min", pressure_min);
- touchData.put("max", pressure_max);
- Settings.System.putString(cr, TOUCH_STATS, touchData.toString());
- } catch (Exception e) {
- Log.e("PlatLogoActivity", "Can't write touch settings", e);
- }
-
- if (maxPointers == 1) {
- tapCount ++;
- if (tapCount < 7) {
- bg.randomizePalette();
- } else {
- launchNextStage();
- }
- } else {
- tapCount = 0;
- }
- maxPointers = 0;
+ v.performClick();
+ // fall through
+ case MotionEvent.ACTION_CANCEL:
+ v.animate().scaleX(1f).scaleY(1f);
+ if (mRotAnim != null) mRotAnim.cancel();
+ testOverlap();
break;
}
return true;
}
- });
+ };
+
+ findViewById(R.id.one).setOnTouchListener(tl);
+ findViewById(R.id.zero).setOnTouchListener(tl);
+ findViewById(R.id.text).setOnTouchListener(tl);
+ }
+
+ private void testOverlap() {
+ final float width = mZeroView.getWidth();
+ final float targetX = mZeroView.getX() + width * .2f;
+ final float targetY = mZeroView.getY() + width * .3f;
+ if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f
+ && Math.abs(mOneView.getRotation() % 360 - 315) < 15) {
+ mOneView.animate().x(mZeroView.getX() + width * .2f);
+ mOneView.animate().y(mZeroView.getY() + width * .3f);
+ mOneView.setRotation(mOneView.getRotation() % 360);
+ mOneView.animate().rotation(315);
+ mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM);
+
+ mBackslash.startAnimating();
+
+ mClicks++;
+ if (mClicks >= 7) {
+ launchNextStage();
+ }
+ } else {
+ mBackslash.stopAnimating();
+ }
}
private void launchNextStage() {
final ContentResolver cr = getContentResolver();
- if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) == 0) {
+ if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
// For posterity: the moment this user unlocked the easter egg
try {
Settings.System.putLong(cr,
- Settings.System.EGG_MODE,
+ "egg_mode", // Settings.System.EGG_MODE,
System.currentTimeMillis());
} catch (RuntimeException e) {
- Log.e("PlatLogoActivity", "Can't write settings", e);
+ Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
}
}
try {
@@ -270,36 +185,206 @@
| Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addCategory("com.android.internal.category.PLATLOGO"));
} catch (ActivityNotFoundException ex) {
- Log.e("PlatLogoActivity", "No more eggs.");
+ Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
}
finish();
}
+ static final String TOUCH_STATS = "touch.stats";
+ double mPressureMin = 0, mPressureMax = -1;
+
+ private void measureTouchPressure(MotionEvent event) {
+ final float pressure = event.getPressure();
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mPressureMax < 0) {
+ mPressureMin = mPressureMax = pressure;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (pressure < mPressureMin) mPressureMin = pressure;
+ if (pressure > mPressureMax) mPressureMax = pressure;
+ break;
+ }
+ }
+
+ private void syncTouchPressure() {
+ try {
+ final String touchDataJson = Settings.System.getString(
+ getContentResolver(), TOUCH_STATS);
+ final JSONObject touchData = new JSONObject(
+ touchDataJson != null ? touchDataJson : "{}");
+ if (touchData.has("min")) {
+ mPressureMin = Math.min(mPressureMin, touchData.getDouble("min"));
+ }
+ if (touchData.has("max")) {
+ mPressureMax = Math.max(mPressureMax, touchData.getDouble("max"));
+ }
+ if (mPressureMax >= 0) {
+ touchData.put("min", mPressureMin);
+ touchData.put("max", mPressureMax);
+ Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString());
+ }
+ } catch (Exception e) {
+ Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e);
+ }
+ }
+
@Override
public void onStart() {
super.onStart();
-
- bg.randomizePalette();
-
- anim = new TimeAnimator();
- anim.setTimeListener(
- new TimeAnimator.TimeListener() {
- @Override
- public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
- bg.setOffset((float) totalTime / 60000f);
- bg.invalidateSelf();
- }
- });
-
- anim.start();
+ syncTouchPressure();
}
@Override
public void onStop() {
- if (anim != null) {
- anim.cancel();
- anim = null;
- }
+ syncTouchPressure();
super.onStop();
}
+
+ static class ZeroDrawable extends Drawable {
+ int mTintColor;
+
+ @Override
+ public void draw(Canvas canvas) {
+ sPaint.setColor(mTintColor | 0xFF000000);
+
+ canvas.save();
+ canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
+
+ canvas.drawCircle(12f, 12f, 10f, sPaint);
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int alpha) { }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) { }
+
+ @Override
+ public void setTintList(ColorStateList tint) {
+ mTintColor = tint.getDefaultColor();
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+
+ static class OneDrawable extends Drawable {
+ int mTintColor;
+
+ @Override
+ public void draw(Canvas canvas) {
+ sPaint.setColor(mTintColor | 0xFF000000);
+
+ canvas.save();
+ canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
+
+ final Path p = new Path();
+ p.moveTo(12f, 21.83f);
+ p.rLineTo(0f, -19.67f);
+ p.rLineTo(-5f, 0f);
+ canvas.drawPath(p, sPaint);
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int alpha) { }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) { }
+
+ @Override
+ public void setTintList(ColorStateList tint) {
+ mTintColor = tint.getDefaultColor();
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+
+ private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener {
+ Bitmap mTile;
+ Paint mPaint = new Paint();
+ BitmapShader mShader;
+ TimeAnimator mAnimator = new TimeAnimator();
+ Matrix mMatrix = new Matrix();
+
+ public void draw(Canvas canvas) {
+ canvas.drawPaint(mPaint);
+ }
+
+ BackslashDrawable(int width) {
+ mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8);
+ mAnimator.setTimeListener(this);
+
+ final Canvas tileCanvas = new Canvas(mTile);
+ final float w = tileCanvas.getWidth();
+ final float h = tileCanvas.getHeight();
+
+ final Path path = new Path();
+ path.moveTo(0, 0);
+ path.lineTo(w / 2, 0);
+ path.lineTo(w, h / 2);
+ path.lineTo(w, h);
+ path.close();
+
+ path.moveTo(0, h / 2);
+ path.lineTo(w / 2, h);
+ path.lineTo(0, h);
+ path.close();
+
+ final Paint slashPaint = new Paint();
+ slashPaint.setAntiAlias(true);
+ slashPaint.setStyle(Paint.Style.FILL);
+ slashPaint.setColor(0xFF000000);
+ tileCanvas.drawPath(path, slashPaint);
+
+ //mPaint.setColor(0xFF0000FF);
+ mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+ mPaint.setShader(mShader);
+ }
+
+ public void startAnimating() {
+ if (!mAnimator.isStarted()) {
+ mAnimator.start();
+ }
+ }
+
+ public void stopAnimating() {
+ if (mAnimator.isStarted()) {
+ mAnimator.cancel();
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ if (mShader != null) {
+ mMatrix.postTranslate(deltaTime / 4f, 0);
+ mShader.setLocalMatrix(mMatrix);
+ invalidateSelf();
+ }
+ }
+ }
}
+
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 4daddfbf..8dc47a4 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -186,6 +186,89 @@
*/
public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED =
"brightline_falsing_manager_enabled";
+ /**
+ * (float) Maximum fraction of the screen required to qualify as a real swipe.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE =
+ "brightline_falsing_distance_screen_fraction_max_distance";
+
+ /**
+ * (float) Multiplier for swipe velocity to convert it to pixels for a fling.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE =
+ "brightline_falsing_distance_velcoity_to_distance";
+
+ /**
+ * (float) How far, in inches, must a fling travel horizontally to qualify as intentional.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN =
+ "brightline_falsing_distance_horizontal_fling_threshold_in";
+
+ /**
+ * (float) Maximum fraction of the screen required to qualify as a real swipe.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN =
+ "brightline_falsing_distance_vertical_fling_threshold_in";
+
+ /**
+ * (float) How far, in inches, must a continuous swipe travel horizontally to be intentional.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN =
+ "brightline_falsing_distance_horizontal_swipe_threshold_in";
+
+ /**
+ * (float) How far, in inches, must a continuous swipe travel vertically to be intentional.
+ */
+ public static final String BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN =
+ "brightline_falsing_distance_horizontal_swipe_threshold_in";
+
+ /**
+ * (float) Percentage of swipe with the proximity sensor covered that triggers a higher
+ * swipe distance requirement.
+ */
+ public static final String BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD =
+ "brightline_falsing_proximity_percent_covered_threshold";
+
+ /**
+ * (float) Angle, in radians, that a swipe can vary from horizontal and sill be intentional.
+ */
+ public static final String BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE =
+ "brightline_falsing_diagonal_horizontal_angle_range";
+
+ /**
+ * (float) Angle, in radians, that a swipe can vary from vertical and sill be intentional.
+ */
+ public static final String BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE =
+ "brightline_falsing_diagonal_horizontal_angle_range";
+
+ /**
+ * (float) Distance, in inches, that a swipe is allowed to vary in the horizontal direction for
+ * horizontal swipes.
+ */
+ public static final String BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE =
+ "brightline_falsing_zigzag_x_primary_deviance";
+
+ /**
+ * (float) Distance, in inches, that a swipe is allowed to vary in the vertical direction for
+ * vertical swipes.
+ */
+ public static final String BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE =
+ "brightline_falsing_zigzag_y_primary_deviance";
+
+ /**
+ * (float) Distance, in inches, that a swipe is allowed to vary in the horizontal direction for
+ * horizontal swipes.
+ */
+ public static final String BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE =
+ "brightline_falsing_zigzag_x_secondary_deviance";
+
+ /**
+ * (float) Distance, in inches, that a swipe is allowed to vary in the vertical direction for
+ * vertical swipes.
+ */
+ public static final String BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE =
+ "brightline_falsing_zigzag_y_secondary_deviance";
+
private SystemUiDeviceConfigFlags() { }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index fe66cf9..7c52a40 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -983,13 +983,14 @@
@Override
public void setBackgroundDrawable(Drawable background) {
-
// TODO: This should route through setWindowBackground, but late in the release to make this
// change.
if (mOriginalBackgroundDrawable != background) {
mOriginalBackgroundDrawable = background;
updateBackgroundDrawable();
- drawableChanged();
+ if (!View.sBrokenWindowBackground) {
+ drawableChanged();
+ }
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fef4dcd..e7e20fc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -724,6 +724,10 @@
<!-- Allows an application to send SMS messages.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.SEND_SMS"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -734,6 +738,10 @@
<!-- Allows an application to receive SMS messages.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.RECEIVE_SMS"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -744,6 +752,10 @@
<!-- Allows an application to read SMS messages.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.READ_SMS"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -754,6 +766,10 @@
<!-- Allows an application to receive WAP push messages.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.RECEIVE_WAP_PUSH"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -763,7 +779,11 @@
android:protectionLevel="dangerous" />
<!-- Allows an application to monitor incoming MMS messages.
- <p>Protection level: dangerous
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.RECEIVE_MMS"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -783,6 +803,11 @@
when the alert is first received, and to delay presenting the info
to the user until after the initial alert dialog is dismissed.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+
@hide Pending API council approval -->
<permission android:name="android.permission.READ_CELL_BROADCASTS"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -805,31 +830,36 @@
android:priority="900" />
<!-- Allows an application to read from external storage.
- <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
- granted this permission.</p>
- <p>This permission is enforced starting in API level 19. Before API level 19, this
- permission is not enforced and all apps still have access to read from external storage.
- You can test your app with the permission enforced by enabling <em>Protect USB
- storage</em> under Developer options in the Settings app on a device running Android 4.1 or
- higher.</p>
- <p>Also starting in API level 19, this permission is <em>not</em> required to
- read/write files in your application-specific directories returned by
- {@link android.content.Context#getExternalFilesDir} and
- {@link android.content.Context#getExternalCacheDir}.
- <p class="note"><strong>Note:</strong> If <em>both</em> your <a
- href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
- minSdkVersion}</a> and <a
- href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
- targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
- grants your app this permission. If you don't need this permission, be sure your <a
- href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
- targetSdkVersion}</a> is 4 or higher.
- <p>Is this permission is not whitelisted for an app that targets an API level before
- {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
- <p>Is this permission is not whitelisted for an app that targets an API level
- {@link android.os.Build.VERSION_CODES#Q} or later the app will be forced into isolated storage.
- </p>
- -->
+ <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
+ granted this permission.</p>
+ <p>This permission is enforced starting in API level 19. Before API level 19, this
+ permission is not enforced and all apps still have access to read from external storage.
+ You can test your app with the permission enforced by enabling <em>Protect USB
+ storage</em> under Developer options in the Settings app on a device running Android 4.1 or
+ higher.</p>
+ <p>Also starting in API level 19, this permission is <em>not</em> required to
+ read/write files in your application-specific directories returned by
+ {@link android.content.Context#getExternalFilesDir} and
+ {@link android.content.Context#getExternalCacheDir}.
+ <p class="note"><strong>Note:</strong> If <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 4 or higher.
+
+ <p> This is a soft restricted permission which cannot be held by an app it its
+ full form until the installer on record whitelists the permission.
+ Specifically, if the permission is whitelisted the holder app can access
+ external storage and the visual and aural media collections while if the
+ permission is not whitelisted the holder app can only access to the visual
+ and aural medial collections. Also the permission is immutably restricted
+ meaning that the whitelist state can be specified only at install time and
+ cannot change until the app is installed. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ <p>Protection level: dangerous -->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_sdcardRead"
@@ -850,8 +880,9 @@
read/write files in your application-specific directories returned by
{@link android.content.Context#getExternalFilesDir} and
{@link android.content.Context#getExternalCacheDir}.
- <p>Is this permission is not whitelisted for an app that targets an API level before
+ <p>If this permission is not whitelisted for an app that targets an API level before
{@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
+ <p>Protection level: dangerous</p>
-->
<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -861,7 +892,8 @@
android:protectionLevel="dangerous" />
<!-- Allows an application to access any geographic locations persisted in the
- user's shared collection. -->
+ user's shared collection.
+ <p>Protection level: dangerous -->
<permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_mediaLocation"
@@ -916,6 +948,10 @@
{@link #ACCESS_FINE_LOCATION}. Requesting this permission by itself doesn't give you
location access.
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -958,6 +994,10 @@
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 16 or higher.</p>
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.READ_CALL_LOG"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -978,6 +1018,10 @@
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
targetSdkVersion}</a> is 16 or higher.</p>
<p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
-->
<permission android:name="android.permission.WRITE_CALL_LOG"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -991,6 +1035,10 @@
abort the call altogether.
<p>Protection level: dangerous
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record whitelists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+
@deprecated Applications should use {@link android.telecom.CallRedirectionService} instead
of the {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL} broadcast.
-->
@@ -1665,7 +1713,7 @@
<!-- Allows applications to pair bluetooth devices without user interaction, and to
allow or disallow phonebook access or message access.
- This is not available to third party applications. -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -2515,7 +2563,8 @@
android:protectionLevel="signature" />
<!-- Allows an application to modify the current configuration, such
- as locale. -->
+ as locale.
+ <p>Protection level: signature|privileged|development -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
android:protectionLevel="signature|privileged|development" />
@@ -2814,7 +2863,8 @@
<!-- ==================================== -->
<eat-comment />
- <!-- Allows access to the list of accounts in the Accounts Service. -->
+ <!-- Allows access to the list of accounts in the Accounts Service.
+ <p>Protection level: signature|privileged -->
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -3393,7 +3443,8 @@
android:protectionLevel="signature" />
<!-- Old permission for deleting an app's cache files, no longer used,
- but signals for us to quietly ignore calls instead of throwing an exception. -->
+ but signals for us to quietly ignore calls instead of throwing an exception.
+ <p>Protection level: signature|privileged -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
android:protectionLevel="signature|privileged" />
@@ -3751,7 +3802,8 @@
<!-- Allows an application to collect component usage
statistics
<p>Declaring the permission implies intention to use the API and the user of the
- device can grant permission through the Settings application. -->
+ device can grant permission through the Settings application.
+ <p>Protection level: signature|privileged|development|appop -->
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
@@ -3774,14 +3826,14 @@
<!-- Permission an application must hold in order to use
{@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
- This is a normal permission: an app requesting it will always be granted the
- permission, without the user needing to approve or see it. -->
+ <p>Protection level: normal -->
<permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
android:label="@string/permlab_requestIgnoreBatteryOptimizations"
android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
android:protectionLevel="normal" />
- <!-- Allows an application to collect battery statistics -->
+ <!-- Allows an application to collect battery statistics
+ <p>Protection level: signature|privileged|development -->
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
@@ -3811,7 +3863,8 @@
android:protectionLevel="signature" />
<!-- Must be required by a {@link android.widget.RemoteViewsService},
- to ensure that only the system can bind to it. -->
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
android:protectionLevel="signature|privileged" />
@@ -3853,7 +3906,8 @@
to the path in the provider where global search queries are
performed. This permission can not be held by regular applications;
it is used by applications to protect themselves from everyone else
- besides global search. -->
+ besides global search.
+ <p>Protection level: signature|privileged -->
<permission android:name="android.permission.GLOBAL_SEARCH"
android:protectionLevel="signature|privileged" />
@@ -4392,7 +4446,8 @@
<permission android:name="android.permission.MODIFY_THEME_OVERLAY"
android:protectionLevel="signature" />
- <!-- Allows an instant app to create foreground services. -->
+ <!-- Allows an instant app to create foreground services.
+ <p>Protection level: signature|development|instant|appop -->
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
android:protectionLevel="signature|development|instant|appop" />
@@ -4462,7 +4517,8 @@
<permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE"
android:protectionLevel="signature|privileged" />
- <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission. -->
+ <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission.
+ <p>Protection level: signature -->
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
@@ -4494,13 +4550,15 @@
<permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
android:protectionLevel="signature" />
- <!-- Allows financial apps to read filtered sms messages. -->
+ <!-- Allows financial apps to read filtered sms messages.
+ Protection level: signature|appop -->
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
android:protectionLevel="signature|appop" />
<!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use
{@link android.app.Notification.Builder#setFullScreenIntent notification full screen
- intents}. -->
+ intents}.
+ <p>Protection level: normal -->
<permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
android:protectionLevel="normal" />
@@ -4616,8 +4674,9 @@
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.PlatLogoActivity"
- android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+ android:theme="@style/Theme.DeviceDefault.DayNight"
android:configChanges="orientation|keyboardHidden"
+ android:icon="@drawable/platlogo"
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
@@ -4939,6 +4998,7 @@
<service
android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:visibleToInstantApps="true"
android:exported="true">
<meta-data
android:name="android.accessibilityservice"
diff --git a/core/res/res/drawable-nodpi/android_logotype.xml b/core/res/res/drawable-nodpi/android_logotype.xml
new file mode 100644
index 0000000..bd298e4
--- /dev/null
+++ b/core/res/res/drawable-nodpi/android_logotype.xml
@@ -0,0 +1,42 @@
+<!--
+Copyright (C) 2015 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="290dp"
+ android:height="64dp"
+ android:viewportWidth="290.0"
+ android:viewportHeight="64.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.81,28.91c-7.44,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37s12.45,-5.85,12.45,-13.37 C34.26,34.76,29.24,28.91,21.81,28.91 M20.13,20.55c6.02,0,11.03,3.09,13.37,6.43v-5.6l9.19,0l0,41.78l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65C31.17,60.91,26.15,64,20.14,64C8.69,64,0,54.23,0,42.28C0,30.33,8.69,20.55,20.13,20.55"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M53.13,21.39l9.19,0l0,5.68c2.5,-4.18,7.27,-6.52,12.7,-6.52c9.69,0,15.96,6.85,15.96,17.46l0,25.15l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95l0,-20.7c0,-6.6,-3.34,-10.61,-8.69,-10.61c-6.1,0,-10.78,4.76,-10.78,13.7l0,20.55l-6.25,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95L53.13,21.39z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M120.06,28.91c-7.43,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37c7.43,0,12.45,-5.85,12.45,-13.37 C132.51,34.76,127.5,28.91,120.06,28.91 M118.39,20.55c6.02,0,11.03,3.09,13.37,6.43l0,-26.49l9.19,0l0,62.66h-6.24 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65c-2.34,3.34,-7.35,6.43,-13.37,6.43c-11.45,0,-20.14,-9.77,-20.14,-21.72 C98.25,30.33,106.94,20.55,118.39,20.55"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M151.39,21.39l9.19,0v7.44c1.59,-4.76,6.27,-7.86,11.03,-7.86c1.17,0,2.34,0.08,3.59,0.34v9.44c-1.59,-0.5,-2.92,-0.75,-4.59,-0.75 c-5.26,0,-10.03,4.43,-10.03,12.78l0,20.39l-6.24,0c-1.63,0,-2.95,-1.32,-2.95,-2.95L151.39,21.39z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M199.98,55.48c7.35,0,12.53,-5.77,12.53,-13.2c0,-7.44,-5.18,-13.2,-12.53,-13.2c-7.44,0,-12.62,5.77,-12.62,13.2 C187.37,49.71,192.55,55.48,199.98,55.48 M199.98,64c-12.37,0,-21.89,-9.61,-21.89,-21.72c0,-12.12,9.52,-21.73,21.89,-21.73 c12.37,0,21.89,9.61,21.89,21.73C221.87,54.39,212.35,64,199.98,64"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M229.32,21.39l9.19,0l0,41.78l-6.24,0c-1.63,0,-2.95,-1.32,-2.95,-2.95L229.32,21.39z M233.92,12.28 c-3.34,0,-6.18,-2.76,-6.18,-6.18c0,-3.34,2.84,-6.1,6.18,-6.1c3.43,0,6.1,2.76,6.1,6.1C240.02,9.53,237.34,12.28,233.92,12.28"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M267.87,28.91c-7.43,0,-12.45,5.85,-12.45,13.37c0,7.52,5.01,13.37,12.45,13.37c7.44,0,12.45,-5.85,12.45,-13.37 C280.32,34.76,275.31,28.91,267.87,28.91 M266.2,20.55c6.02,0,11.03,3.09,13.37,6.43l0,-26.49l9.19,0l0,62.66l-6.24,0 c-1.63,0,-2.95,-1.32,-2.95,-2.95v-2.65c-2.34,3.34,-7.35,6.43,-13.37,6.43c-11.44,0,-20.14,-9.77,-20.14,-21.72S254.76,20.55,266.2,20.55"/>
+</vector>
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index f5bbadc..19a296a 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,5 +1,5 @@
<!--
-Copyright (C) 2018 The Android Open Source Project
+Copyright (C) 2015 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,21 +13,15 @@
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:name="vector"
- android:width="640dp"
- android:height="640dp"
- android:viewportWidth="64"
- android:viewportHeight="64">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:name="bg"
- android:pathData="M 27 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64"
- android:strokeColor="#6823a1"
- android:strokeWidth="16"/>
+ android:fillColor="#FF000000"
+ android:pathData="M19.45,22.89l-10.250001,-10.249999l-2.6599998,2.6599998l-1.77,-1.7600002l4.43,-4.4300003l12.0199995,12.0199995l-1.7699986,1.7600002z"/>
<path
- android:name="fg"
- android:pathData="M 29 43 L 32 43 C 38.075 43 43 38.075 43 32 C 43 25.925 38.075 21 32 21 C 25.925 21 21 25.925 21 32 L 21 64"
- android:strokeColor="#ff0000"
- android:strokeWidth="8"/>
+ android:fillColor="#FF000000"
+ android:pathData="M12,6a6,6 0,1 1,-6 6,6 6,0 0,1 6,-6m0,-2.5A8.5,8.5 0,1 0,20.5 12,8.51 8.51,0 0,0 12,3.5Z"/>
</vector>
diff --git a/core/res/res/layout/platlogo_layout.xml b/core/res/res/layout/platlogo_layout.xml
new file mode 100644
index 0000000..4a4ad75
--- /dev/null
+++ b/core/res/res/layout/platlogo_layout.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:background="@android:color/transparent">
+ <ImageView
+ android:id="@+id/text"
+ android:layout_width="400dp"
+ android:layout_height="wrap_content"
+ android:translationY="-100dp"
+ android:adjustViewBounds="true"
+ android:layout_marginBottom="-80dp"
+ android:layout_centerInParent="true"
+ android:src="@drawable/android_logotype"
+ android:tint="?android:attr/textColorPrimary"
+ />
+ <ImageView
+ android:id="@+id/one"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ android:layout_marginLeft="24dp"
+ android:layout_below="@id/text"
+ android:layout_alignLeft="@id/text"
+ android:tint="?android:attr/textColorPrimary"
+ />
+ <ImageView
+ android:id="@+id/zero"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ android:layout_marginRight="34dp"
+ android:layout_below="@id/text"
+ android:layout_alignRight="@id/text"
+ android:tint="?android:attr/textColorPrimary"
+ />
+</RelativeLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f92a58a..eb09d3f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4162,6 +4162,10 @@
one bar higher than they actually are -->
<bool name="config_inflateSignalStrength">false</bool>
+ <!-- Contains a blacklist of apps that should not get pre-installed carrier app permission
+ grants, even if the UICC claims that the app should be privileged. See b/138150105 -->
+ <string-array name="config_restrictedPreinstalledCarrierApps" translatable="false"/>
+
<!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q -->
<integer name="config_maxShortcutTargetsPerApp">3</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c10d32c..646c4ce 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3819,4 +3819,9 @@
<java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
<java-symbol type="bool" name="config_inflateSignalStrength" />
+ <java-symbol type="array" name="config_restrictedPreinstalledCarrierApps" />
+
+ <java-symbol type="drawable" name="android_logotype" />
+ <java-symbol type="layout" name="platlogo_layout" />
+
</resources>
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index d5163e1..eff4826 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -64,7 +64,7 @@
@Test
public void testSingleByte() throws Exception {
final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
- new long[] { 10, 11 }).getFileDescriptor();
+ new long[] { 10, 11 }, new long[] {}).getFileDescriptor();
final byte[] buf = new byte[1_000];
assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
@@ -80,7 +80,7 @@
@Test
public void testRanges() throws Exception {
final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
- new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+ new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor();
final byte[] buf = new byte[10];
assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
@@ -102,7 +102,7 @@
@Test
public void testEntireFile() throws Exception {
final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
- new long[] { 0, 5_000_000 }).getFileDescriptor();
+ new long[] { 0, 5_000_000 }, new long[] {}).getFileDescriptor();
try (FileInputStream in = new FileInputStream(fd)) {
int val;
@@ -115,7 +115,7 @@
@Test
public void testReadWrite() throws Exception {
final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
- new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+ new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor();
// Redacted at first
final byte[] buf = new byte[10];
@@ -168,4 +168,76 @@
assertArrayEquals(new long[] { 100, 200 },
removeRange(new long[] { 100, 200 }, 150, 150));
}
+
+ @Test
+ public void testFreeAtStart() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 10 }, new long[] {1}).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0 },
+ buf);
+ }
+
+ @Test
+ public void testFreeAtOffset() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 10 }, new long[] {3}).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, 0, 0, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0 },
+ buf);
+ }
+
+ @Test
+ public void testFreeAcrossRedactionStart() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 10 }, new long[] {0}).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0, 0 },
+ buf);
+ }
+
+ @Test
+ public void testFreeAcrossRedactionEnd() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 3 }, new long[] {2}).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, 0, (byte) 'f', 64, 64, 64, 64, 64, 64, 64 },
+ buf);
+ }
+
+ @Test
+ public void testFreeOutsideRedaction() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 8 }, new long[] { 8 }).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, 0, 0, 0, 0, 0, 0, 0, 64, 64 },
+ buf);
+ }
+
+ @Test
+ public void testFreeMultipleRedactions() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+ new long[] { 1, 2, 3, 4 }, new long[] { 0 }).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0));
+ assertArrayEquals(
+ new byte[] { 64, (byte) 'r', 64, (byte) 'e', 64, 64, 64, 64, 64, 64 },
+ buf);
+ }
}
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 3361fa2..1d294d5 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -27,7 +27,7 @@
/**
* RectF holds four float coordinates for a rectangle. The rectangle is
- * represented by the coordinates of its 4 edges (left, top, right bottom).
+ * represented by the coordinates of its 4 edges (left, top, right, bottom).
* These fields can be accessed directly. Use width() and height() to retrieve
* the rectangle's width and height. Note: most methods do not check to see that
* the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index c7dd40d..7f76a45 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -1,40 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 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.egg"
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="28" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
- android:icon="@drawable/icon"
+ android:icon="@drawable/q_icon"
android:label="@string/app_name">
+ <activity android:name=".quares.QuaresActivity"
+ android:icon="@drawable/q_icon"
+ android:label="@string/q_egg_name"
+ android:theme="@style/QuaresTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <!-- <category android:name="android.intent.category.LAUNCHER" /> -->
+ <category android:name="com.android.internal.category.PLATLOGO" />
+ </intent-filter>
+ </activity>
<activity
android:name=".paint.PaintActivity"
android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
- android:label="@string/app_name"
+ android:icon="@drawable/p_icon"
+ android:label="@string/p_egg_name"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <!--<category android:name="android.intent.category.LAUNCHER" />-->
- <category android:name="com.android.internal.category.PLATLOGO" />
+
+ <!-- <category android:name="android.intent.category.DEFAULT" /> -->
+ <!-- <category android:name="android.intent.category.LAUNCHER" /> -->
+ <!-- <category android:name="com.android.internal.category.PLATLOGO" /> -->
</intent-filter>
</activity>
</application>
diff --git a/packages/EasterEgg/res/drawable/icon_bg.xml b/packages/EasterEgg/res/drawable/icon_bg.xml
index c1553ce..659f98b 100644
--- a/packages/EasterEgg/res/drawable/icon_bg.xml
+++ b/packages/EasterEgg/res/drawable/icon_bg.xml
@@ -15,4 +15,4 @@
limitations under the License.
-->
<color xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#C5E1A5" />
\ No newline at end of file
+ android:color="@color/q_clue_text" />
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/p_icon.xml
similarity index 100%
rename from packages/EasterEgg/res/drawable/icon.xml
rename to packages/EasterEgg/res/drawable/p_icon.xml
diff --git a/packages/EasterEgg/res/drawable/pixel_bg.xml b/packages/EasterEgg/res/drawable/pixel_bg.xml
new file mode 100644
index 0000000..4d4a113
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/pixel_bg.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:exitFadeDuration="100">
+ <item android:state_pressed="true">
+ <shape><solid android:color="@color/red"/></shape>
+ </item>
+ <item android:state_checked="true">
+ <shape><solid android:color="@color/pixel_on"/></shape>
+ </item>
+ <item>
+ <shape><solid android:color="@color/pixel_off"/></shape>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/q.xml b/packages/EasterEgg/res/drawable/q.xml
new file mode 100644
index 0000000..75baa47
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/q.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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">
+ <path
+ android:fillColor="@color/q_icon_fg"
+ android:pathData="M19.45,22.89l-10.250001,-10.249999l-2.6599998,2.6599998l-1.77,-1.7600002l4.43,-4.4300003l12.0199995,12.0199995l-1.7699986,1.7600002z"/>
+ <path
+ android:fillColor="@color/q_icon_fg"
+ android:pathData="M12,6a6,6 0,1 1,-6 6,6 6,0 0,1 6,-6m0,-2.5A8.5,8.5 0,1 0,20.5 12,8.51 8.51,0 0,0 12,3.5Z"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/q_icon.xml b/packages/EasterEgg/res/drawable/q_icon.xml
new file mode 100644
index 0000000..ef4b0a3
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/q_icon.xml
@@ -0,0 +1,19 @@
+<!--
+ 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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/icon_bg"/>
+ <foreground android:drawable="@drawable/q_smaller"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/drawable/q_smaller.xml b/packages/EasterEgg/res/drawable/q_smaller.xml
new file mode 100644
index 0000000..c71dff0
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/q_smaller.xml
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:insetBottom="5dp"
+ android:insetLeft="5dp"
+ android:insetRight="5dp"
+ android:insetTop="5dp"
+ android:drawable="@drawable/q" />
diff --git a/packages/EasterEgg/res/layout/activity_quares.xml b/packages/EasterEgg/res/layout/activity_quares.xml
new file mode 100644
index 0000000..dcc90f6
--- /dev/null
+++ b/packages/EasterEgg/res/layout/activity_quares.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:animateLayoutChanges="true"
+ tools:context="com.android.egg.quares.QuaresActivity">
+
+ <GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:alignmentMode="alignBounds"
+ android:id="@+id/grid"
+ />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/label"
+ android:layout_gravity="center_horizontal|bottom"
+ android:gravity="center"
+ android:textSize="18dp"
+ android:visibility="gone"
+ android:drawablePadding="8dp"
+ android:padding="12dp"
+ android:backgroundTint="@color/q_clue_bg_correct"
+ android:textColor="@color/q_clue_text"
+ android:layout_marginBottom="48dp"
+ android:elevation="30dp"
+ />
+</FrameLayout>
diff --git a/packages/EasterEgg/res/values-night/q_colors.xml b/packages/EasterEgg/res/values-night/q_colors.xml
new file mode 100644
index 0000000..191bd94
--- /dev/null
+++ b/packages/EasterEgg/res/values-night/q_colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<resources>
+ <color name="pixel_off">#000000</color>
+ <color name="pixel_on">#FFFFFF</color>
+
+ <color name="q_clue_bg">@color/navy</color>
+ <color name="q_clue_text">@color/tan</color>
+</resources>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values/q_colors.xml b/packages/EasterEgg/res/values/q_colors.xml
new file mode 100644
index 0000000..5e92c84
--- /dev/null
+++ b/packages/EasterEgg/res/values/q_colors.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<resources>
+ <color name="emerald">#3ddc84</color>
+ <color name="red">#f8c734</color>
+ <color name="navy">#073042</color>
+ <color name="vapor">#d7effe</color>
+ <color name="tan">#eff7cf</color>
+
+ <color name="pixel_off">#FFFFFF</color>
+ <color name="pixel_on">#000000</color>
+
+ <color name="q_clue_bg">@color/tan</color>
+ <color name="q_clue_text">@color/navy</color>
+ <color name="q_clue_bg_correct">@color/emerald</color>
+
+ <color name="q_icon_fg">@color/emerald</color>
+</resources>
diff --git a/packages/EasterEgg/res/values/q_puzzles.xml b/packages/EasterEgg/res/values/q_puzzles.xml
new file mode 100644
index 0000000..7c2eff1
--- /dev/null
+++ b/packages/EasterEgg/res/values/q_puzzles.xml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string-array name="puzzles">
+
+ <item>q</item>
+ <item>q</item>
+ <item>q</item>
+ <item>q</item>
+ <item>q</item>
+
+ <item>android:drawable/ic_info</item>
+
+ <item>android:drawable/stat_sys_adb</item>
+ <item>android:drawable/stat_sys_battery</item>
+ <item>android:drawable/stat_sys_phone_call</item>
+ <item>android:drawable/stat_sys_certificate_info</item>
+ <item>android:drawable/stat_sys_data_bluetooth</item>
+ <item>android:drawable/stat_sys_data_usb</item>
+ <item>android:drawable/stat_sys_download</item>
+ <item>android:drawable/stat_sys_gps_on</item>
+ <item>android:drawable/stat_sys_phone_call</item>
+ <item>android:drawable/stat_sys_tether_wifi</item>
+ <item>android:drawable/stat_sys_throttled</item>
+ <item>android:drawable/stat_sys_upload</item>
+
+ <item>android:drawable/stat_notify_car_mode</item>
+ <item>android:drawable/stat_notify_chat</item>
+ <item>android:drawable/stat_notify_disk_full</item>
+ <item>android:drawable/stat_notify_email_generic</item>
+ <item>android:drawable/stat_notify_error</item>
+ <item>android:drawable/stat_notify_gmail</item>
+ <item>android:drawable/stat_notify_missed_call</item>
+ <item>android:drawable/stat_notify_mmcc_indication_icn</item>
+ <item>android:drawable/stat_notify_more</item>
+ <item>android:drawable/stat_notify_rssi_in_range</item>
+ <item>android:drawable/stat_notify_sdcard</item>
+ <item>android:drawable/stat_notify_sdcard_prepare</item>
+ <item>android:drawable/stat_notify_sdcard_usb</item>
+ <item>android:drawable/stat_notify_sim_toolkit</item>
+ <item>android:drawable/stat_notify_sync</item>
+ <item>android:drawable/stat_notify_sync_anim0</item>
+ <item>android:drawable/stat_notify_sync_error</item>
+ <item>android:drawable/stat_notify_voicemail</item>
+
+ <item>android:drawable/ic_audio_alarm</item>
+ <item>android:drawable/ic_audio_alarm_mute</item>
+ <item>android:drawable/ic_bluetooth_share_icon</item>
+ <item>android:drawable/ic_bt_headphones_a2dp</item>
+ <item>android:drawable/ic_bt_headset_hfp</item>
+ <item>android:drawable/ic_bt_hearing_aid</item>
+ <item>android:drawable/ic_bt_laptop</item>
+ <item>android:drawable/ic_bt_misc_hid</item>
+ <item>android:drawable/ic_bt_network_pan</item>
+ <item>android:drawable/ic_bt_pointing_hid</item>
+ <item>android:drawable/ic_corp_badge</item>
+ <item>android:drawable/ic_expand_more</item>
+ <item>android:drawable/ic_faster_emergency</item>
+ <item>android:drawable/ic_file_copy</item>
+ <item>android:drawable/ic_info_outline_24</item>
+ <item>android:drawable/ic_lock</item>
+ <item>android:drawable/ic_lock_bugreport</item>
+ <item>android:drawable/ic_lock_open</item>
+ <item>android:drawable/ic_lock_power_off</item>
+ <item>android:drawable/ic_lockscreen_ime</item>
+ <item>android:drawable/ic_mode_edit</item>
+ <item>android:drawable/ic_phone</item>
+ <item>android:drawable/ic_qs_airplane</item>
+ <item>android:drawable/ic_qs_auto_rotate</item>
+ <item>android:drawable/ic_qs_battery_saver</item>
+ <item>android:drawable/ic_qs_bluetooth</item>
+ <item>android:drawable/ic_qs_dnd</item>
+ <item>android:drawable/ic_qs_flashlight</item>
+ <item>android:drawable/ic_qs_night_display_on</item>
+ <item>android:drawable/ic_restart</item>
+ <item>android:drawable/ic_screenshot</item>
+ <item>android:drawable/ic_settings_bluetooth</item>
+ <item>android:drawable/ic_signal_cellular_0_4_bar</item>
+ <item>android:drawable/ic_signal_cellular_0_5_bar</item>
+ <item>android:drawable/ic_signal_cellular_1_4_bar</item>
+ <item>android:drawable/ic_signal_cellular_1_5_bar</item>
+ <item>android:drawable/ic_signal_cellular_2_4_bar</item>
+ <item>android:drawable/ic_signal_cellular_2_5_bar</item>
+ <item>android:drawable/ic_signal_cellular_3_4_bar</item>
+ <item>android:drawable/ic_signal_cellular_3_5_bar</item>
+ <item>android:drawable/ic_signal_cellular_4_4_bar</item>
+ <item>android:drawable/ic_signal_cellular_4_5_bar</item>
+ <item>android:drawable/ic_signal_cellular_5_5_bar</item>
+ <item>android:drawable/ic_signal_location</item>
+ <item>android:drawable/ic_wifi_signal_0</item>
+ <item>android:drawable/ic_wifi_signal_1</item>
+ <item>android:drawable/ic_wifi_signal_2</item>
+ <item>android:drawable/ic_wifi_signal_3</item>
+ <item>android:drawable/ic_wifi_signal_4</item>
+ <item>android:drawable/perm_group_activity_recognition</item>
+ <item>android:drawable/perm_group_calendar</item>
+ <item>android:drawable/perm_group_call_log</item>
+ <item>android:drawable/perm_group_camera</item>
+ <item>android:drawable/perm_group_contacts</item>
+ <item>android:drawable/perm_group_location</item>
+ <item>android:drawable/perm_group_microphone</item>
+ <item>android:drawable/perm_group_phone_calls</item>
+ <item>android:drawable/perm_group_sensors</item>
+ <item>android:drawable/perm_group_sms</item>
+ <item>android:drawable/perm_group_storage</item>
+ <item>android:drawable/perm_group_visual</item>
+
+ <item>com.android.settings:drawable/ic_add_24dp</item>
+ <item>com.android.settings:drawable/ic_airplanemode_active</item>
+ <item>com.android.settings:drawable/ic_android</item>
+ <item>com.android.settings:drawable/ic_apps</item>
+ <item>com.android.settings:drawable/ic_arrow_back</item>
+ <item>com.android.settings:drawable/ic_arrow_down_24dp</item>
+ <item>com.android.settings:drawable/ic_battery_charging_full</item>
+ <item>com.android.settings:drawable/ic_battery_status_bad_24dp</item>
+ <item>com.android.settings:drawable/ic_battery_status_good_24dp</item>
+ <item>com.android.settings:drawable/ic_battery_status_maybe_24dp</item>
+ <item>com.android.settings:drawable/ic_call_24dp</item>
+ <item>com.android.settings:drawable/ic_cancel</item>
+ <item>com.android.settings:drawable/ic_cast_24dp</item>
+ <item>com.android.settings:drawable/ic_chevron_right_24dp</item>
+ <item>com.android.settings:drawable/ic_data_saver</item>
+ <item>com.android.settings:drawable/ic_delete</item>
+ <item>com.android.settings:drawable/ic_devices_other</item>
+ <item>com.android.settings:drawable/ic_devices_other_opaque_black</item>
+ <item>com.android.settings:drawable/ic_do_not_disturb_on_24dp</item>
+ <item>com.android.settings:drawable/ic_eject_24dp</item>
+ <item>com.android.settings:drawable/ic_expand_less</item>
+ <item>com.android.settings:drawable/ic_expand_more_inverse</item>
+ <item>com.android.settings:drawable/ic_folder_vd_theme_24</item>
+ <item>com.android.settings:drawable/ic_friction_lock_closed</item>
+ <item>com.android.settings:drawable/ic_gray_scale_24dp</item>
+ <item>com.android.settings:drawable/ic_headset_24dp</item>
+ <item>com.android.settings:drawable/ic_help</item>
+ <item>com.android.settings:drawable/ic_local_movies</item>
+ <item>com.android.settings:drawable/ic_lock</item>
+ <item>com.android.settings:drawable/ic_media_stream</item>
+ <item>com.android.settings:drawable/ic_network_cell</item>
+ <item>com.android.settings:drawable/ic_notifications</item>
+ <item>com.android.settings:drawable/ic_notifications_off_24dp</item>
+ <item>com.android.settings:drawable/ic_phone_info</item>
+ <item>com.android.settings:drawable/ic_photo_library</item>
+ <item>com.android.settings:drawable/ic_settings_accessibility</item>
+ <item>com.android.settings:drawable/ic_settings_accounts</item>
+ <item>com.android.settings:drawable/ic_settings_backup</item>
+ <item>com.android.settings:drawable/ic_settings_battery_white</item>
+ <item>com.android.settings:drawable/ic_settings_data_usage</item>
+ <item>com.android.settings:drawable/ic_settings_date_time</item>
+ <item>com.android.settings:drawable/ic_settings_delete</item>
+ <item>com.android.settings:drawable/ic_settings_display_white</item>
+ <item>com.android.settings:drawable/ic_settings_home</item>
+ <item>com.android.settings:drawable/ic_settings_location</item>
+ <item>com.android.settings:drawable/ic_settings_night_display</item>
+ <item>com.android.settings:drawable/ic_settings_open</item>
+ <item>com.android.settings:drawable/ic_settings_print</item>
+ <item>com.android.settings:drawable/ic_settings_privacy</item>
+ <item>com.android.settings:drawable/ic_settings_security_white</item>
+ <item>com.android.settings:drawable/ic_settings_sim</item>
+ <item>com.android.settings:drawable/ic_settings_wireless</item>
+ <item>com.android.settings:drawable/ic_storage</item>
+ <item>com.android.settings:drawable/ic_storage_white</item>
+ <item>com.android.settings:drawable/ic_suggestion_night_display</item>
+ <item>com.android.settings:drawable/ic_sync</item>
+ <item>com.android.settings:drawable/ic_system_update</item>
+ <item>com.android.settings:drawable/ic_videogame_vd_theme_24</item>
+ <item>com.android.settings:drawable/ic_volume_ringer_vibrate</item>
+ <item>com.android.settings:drawable/ic_volume_up_24dp</item>
+ <item>com.android.settings:drawable/ic_vpn_key</item>
+ <item>com.android.settings:drawable/ic_wifi_tethering</item>
+
+ <item>com.android.systemui:drawable/ic_alarm</item>
+ <item>com.android.systemui:drawable/ic_alarm_dim</item>
+ <item>com.android.systemui:drawable/ic_arrow_back</item>
+ <item>com.android.systemui:drawable/ic_bluetooth_connected</item>
+ <item>com.android.systemui:drawable/ic_brightness_thumb</item>
+ <item>com.android.systemui:drawable/ic_camera</item>
+ <item>com.android.systemui:drawable/ic_cast</item>
+ <item>com.android.systemui:drawable/ic_cast_connected</item>
+ <item>com.android.systemui:drawable/ic_cast_connected_fill</item>
+ <item>com.android.systemui:drawable/ic_close_white</item>
+ <item>com.android.systemui:drawable/ic_data_saver</item>
+ <item>com.android.systemui:drawable/ic_data_saver_off</item>
+ <item>com.android.systemui:drawable/ic_drag_handle</item>
+ <item>com.android.systemui:drawable/ic_headset</item>
+ <item>com.android.systemui:drawable/ic_headset_mic</item>
+ <item>com.android.systemui:drawable/ic_hotspot</item>
+ <item>com.android.systemui:drawable/ic_invert_colors</item>
+ <item>com.android.systemui:drawable/ic_location</item>
+ <item>com.android.systemui:drawable/ic_lockscreen_ime</item>
+ <item>com.android.systemui:drawable/ic_notifications_alert</item>
+ <item>com.android.systemui:drawable/ic_notifications_silence</item>
+ <item>com.android.systemui:drawable/ic_power_low</item>
+ <item>com.android.systemui:drawable/ic_power_saver</item>
+ <item>com.android.systemui:drawable/ic_qs_bluetooth_connecting</item>
+ <item>com.android.systemui:drawable/ic_qs_bluetooth_on</item>
+ <item>com.android.systemui:drawable/ic_qs_cancel</item>
+ <item>com.android.systemui:drawable/ic_qs_no_sim</item>
+ <item>com.android.systemui:drawable/ic_screenshot_delete</item>
+ <item>com.android.systemui:drawable/ic_settings</item>
+ <item>com.android.systemui:drawable/ic_swap_vert</item>
+ <item>com.android.systemui:drawable/ic_volume_alarm</item>
+ <item>com.android.systemui:drawable/ic_volume_alarm_mute</item>
+ <item>com.android.systemui:drawable/ic_volume_media</item>
+ <item>com.android.systemui:drawable/ic_volume_media_mute</item>
+ <item>com.android.systemui:drawable/ic_volume_ringer</item>
+ <item>com.android.systemui:drawable/ic_volume_ringer_mute</item>
+ <item>com.android.systemui:drawable/ic_volume_ringer_vibrate</item>
+ <item>com.android.systemui:drawable/ic_volume_voice</item>
+ <item>com.android.systemui:drawable/stat_sys_camera</item>
+ <item>com.android.systemui:drawable/stat_sys_managed_profile_status</item>
+ <item>com.android.systemui:drawable/stat_sys_mic_none</item>
+ <item>com.android.systemui:drawable/stat_sys_vpn_ic</item>
+
+ </string-array>
+</resources>
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
index 32dbc97..b95ec6b 100644
--- a/packages/EasterEgg/res/values/strings.xml
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,5 +14,11 @@
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <string name="app_name" translatable="false">PAINT.APK</string>
+ <string name="app_name" translatable="false">Android Q Easter Egg</string>
+
+ <!-- name of the Q easter egg, a nonogram-style icon puzzle -->
+ <string name="q_egg_name" translatable="false">Icon Quiz</string>
+
+ <!-- name of the P easter egg, a humble paint program -->
+ <string name="p_egg_name" translatable="false">PAINT.APK</string>
</resources>
diff --git a/packages/EasterEgg/res/values/styles.xml b/packages/EasterEgg/res/values/styles.xml
index 44e2ce5..e576526 100644
--- a/packages/EasterEgg/res/values/styles.xml
+++ b/packages/EasterEgg/res/values/styles.xml
@@ -20,4 +20,16 @@
<item name="android:windowLightNavigationBar">true</item>
</style>
+ <style name="QuaresTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
</resources>
diff --git a/packages/EasterEgg/src/com/android/egg/quares/Quare.kt b/packages/EasterEgg/src/com/android/egg/quares/Quare.kt
new file mode 100644
index 0000000..eb77362
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/quares/Quare.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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.egg.quares
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.os.Parcel
+import android.os.Parcelable
+import java.util.ArrayList
+import kotlin.math.abs
+import kotlin.math.round
+
+class Quare(val width: Int, val height: Int, val depth: Int) : Parcelable {
+ private val data: IntArray = IntArray(width * height)
+ private val user: IntArray = data.copyOf()
+
+ private fun loadAndQuantize(bitmap8bpp: Bitmap) {
+ bitmap8bpp.getPixels(data, 0, width, 0, 0, width, height)
+ if (depth == 8) return
+ val s = (255f / depth)
+ for (i in 0 until data.size) {
+ var f = (data[i] ushr 24).toFloat() / s
+ // f = f.pow(0.75f) // gamma adjust for bolder lines
+ f *= 1.25f // brightness adjust for bolder lines
+ f.coerceAtMost(1f)
+ data[i] = (round(f) * s).toInt() shl 24
+ }
+ }
+
+ fun isBlank(): Boolean {
+ return data.sum() == 0
+ }
+
+ fun load(drawable: Drawable) {
+ val resized = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
+ val canvas = Canvas(resized)
+ drawable.setBounds(0, 0, width, height)
+ drawable.setTint(0xFF000000.toInt())
+ drawable.draw(canvas)
+ loadAndQuantize(resized)
+ resized.recycle()
+ }
+
+ fun load(context: Context, icon: Icon) {
+ icon.loadDrawable(context)?.let {
+ load(it)
+ }
+ }
+
+ fun bitmap(): Bitmap {
+ return Bitmap.createBitmap(data, width, height, Bitmap.Config.ALPHA_8)
+ }
+
+ fun getUserMark(x: Int, y: Int): Int {
+ return user[y * width + x] ushr 24
+ }
+
+ fun setUserMark(x: Int, y: Int, v: Int) {
+ user[y * width + x] = v shl 24
+ }
+
+ fun getDataAt(x: Int, y: Int): Int {
+ return data[y * width + x] ushr 24
+ }
+
+ fun check(): Boolean {
+ return data.contentEquals(user)
+ }
+
+ fun check(xSel: Int, ySel: Int): Boolean {
+ val xStart = if (xSel < 0) 0 else xSel
+ val xEnd = if (xSel < 0) width - 1 else xSel
+ val yStart = if (ySel < 0) 0 else ySel
+ val yEnd = if (ySel < 0) height - 1 else ySel
+ for (y in yStart..yEnd)
+ for (x in xStart..xEnd)
+ if (getDataAt(x, y) != getUserMark(x, y)) return false
+ return true
+ }
+
+ fun errors(): IntArray {
+ return IntArray(width * height) {
+ abs(data[it] - user[it])
+ }
+ }
+
+ fun getRowClue(y: Int): IntArray {
+ return getClue(-1, y)
+ }
+ fun getColumnClue(x: Int): IntArray {
+ return getClue(x, -1)
+ }
+ fun getClue(xSel: Int, ySel: Int): IntArray {
+ val arr = ArrayList<Int>()
+ var len = 0
+ val xStart = if (xSel < 0) 0 else xSel
+ val xEnd = if (xSel < 0) width - 1 else xSel
+ val yStart = if (ySel < 0) 0 else ySel
+ val yEnd = if (ySel < 0) height - 1 else ySel
+ for (y in yStart..yEnd)
+ for (x in xStart..xEnd)
+ if (getDataAt(x, y) != 0) {
+ len++
+ } else if (len > 0) {
+ arr.add(len)
+ len = 0
+ }
+ if (len > 0) arr.add(len)
+ else if (arr.size == 0) arr.add(0)
+ return arr.toIntArray()
+ }
+
+ fun resetUserMarks() {
+ user.forEachIndexed { index, _ -> user[index] = 0 }
+ }
+
+ // Parcelable interface
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun writeToParcel(p: Parcel?, flags: Int) {
+ p?.let {
+ p.writeInt(width)
+ p.writeInt(height)
+ p.writeInt(depth)
+ p.writeIntArray(data)
+ p.writeIntArray(user)
+ }
+ }
+
+ companion object CREATOR : Parcelable.Creator<Quare> {
+ override fun createFromParcel(p: Parcel?): Quare {
+ return p!!.let {
+ Quare(
+ p.readInt(), // width
+ p.readInt(), // height
+ p.readInt() // depth
+ ).also {
+ p.readIntArray(it.data)
+ p.readIntArray(it.user)
+ }
+ }
+ }
+
+ override fun newArray(size: Int): Array<Quare?> {
+ return arrayOfNulls(size)
+ }
+ }
+}
diff --git a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
new file mode 100644
index 0000000..ce439a9
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright 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.egg.quares
+
+import android.app.Activity
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Typeface
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import android.text.StaticLayout
+import android.text.TextPaint
+import android.util.Log
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.widget.Button
+import android.widget.CompoundButton
+import android.widget.GridLayout
+
+import java.util.Random
+
+import com.android.egg.R
+
+const val TAG = "Quares"
+
+class QuaresActivity : Activity() {
+ private var q: Quare = Quare(16, 16, 1)
+ private var resId = 0
+ private var resName = ""
+ private var icon: Icon? = null
+
+ private lateinit var label: Button
+ private lateinit var grid: GridLayout
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ window.decorView.systemUiVisibility =
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+
+ actionBar?.hide()
+
+ setContentView(R.layout.activity_quares)
+
+ grid = findViewById(R.id.grid)
+ label = findViewById(R.id.label)
+
+ if (savedInstanceState != null) {
+ Log.v(TAG, "restoring puzzle from state")
+ q = savedInstanceState.getParcelable("q") ?: q
+ resId = savedInstanceState.getInt("resId")
+ resName = savedInstanceState.getString("resName", "")
+ loadPuzzle()
+ }
+
+ label.setOnClickListener { newPuzzle() }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (resId == 0) {
+ // lazy init from onCreate
+ newPuzzle()
+ }
+ checkVictory()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+
+ outState.putParcelable("q", q)
+ outState.putInt("resId", resId)
+ outState.putString("resName", resName)
+ }
+
+ fun newPuzzle() {
+ Log.v(TAG, "new puzzle...")
+
+ q.resetUserMarks()
+ val oldResId = resId
+ resId = android.R.drawable.stat_sys_warning
+ try {
+ for (tries in 0..3) {
+ val ar = resources.obtainTypedArray(R.array.puzzles)
+ val newName = ar.getString(Random().nextInt(ar.length()))
+ if (newName == null) continue
+
+ Log.v(TAG, "Looking for icon " + newName)
+
+ val pkg = getPackageNameForResourceName(newName)
+ val newId = packageManager.getResourcesForApplication(pkg)
+ .getIdentifier(newName, "drawable", pkg)
+ if (newId == 0) {
+ Log.v(TAG, "oops, " + newName + " doesn't resolve from pkg " + pkg)
+ } else if (newId != oldResId) {
+ // got a good one
+ resId = newId
+ resName = newName
+ break
+ }
+ }
+ } catch (e: RuntimeException) {
+ Log.v(TAG, "problem loading puzzle, using fallback", e)
+ }
+ loadPuzzle()
+ }
+
+ fun getPackageNameForResourceName(name: String): String {
+ return if (name.contains(":") && !name.startsWith("android:")) {
+ name.substring(0, name.indexOf(":"))
+ } else {
+ packageName
+ }
+ }
+
+ fun checkVictory() {
+ if (q.check()) {
+ val dp = resources.displayMetrics.density
+
+ val label: Button = findViewById(R.id.label)
+ label.text = resName.replace(Regex("^.*/"), "")
+ val drawable = icon?.loadDrawable(this)?.also {
+ it.setBounds(0, 0, (32 * dp).toInt(), (32 * dp).toInt())
+ it.setTint(label.currentTextColor)
+ }
+ label.setCompoundDrawables(drawable, null, null, null)
+
+ label.visibility = VISIBLE
+ } else {
+ label.visibility = GONE
+ }
+ }
+
+ fun loadPuzzle() {
+ Log.v(TAG, "loading " + resName + " at " + q.width + "x" + q.height)
+
+ val dp = resources.displayMetrics.density
+
+ icon = Icon.createWithResource(getPackageNameForResourceName(resName), resId)
+ q.load(this, icon!!)
+
+ if (q.isBlank()) {
+ // this is a really boring puzzle, let's try again
+ resId = 0
+ resName = ""
+ recreate()
+ return
+ }
+
+ grid.removeAllViews()
+ grid.columnCount = q.width + 1
+ grid.rowCount = q.height + 1
+
+ label.visibility = GONE
+
+ val orientation = resources.configuration.orientation
+
+ // clean this up a bit
+ val minSide = resources.configuration.smallestScreenWidthDp - 25 // ish
+ val size = (minSide / (q.height + 0.5) * dp).toInt()
+
+ val sb = StringBuffer()
+
+ for (j in 0 until grid.rowCount) {
+ for (i in 0 until grid.columnCount) {
+ val tv: View
+ val params = GridLayout.LayoutParams().also {
+ it.width = size
+ it.height = size
+ it.setMargins(1, 1, 1, 1)
+ it.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, GridLayout.TOP) // UGH
+ }
+ val x = i - 1
+ val y = j - 1
+ if (i > 0 && j > 0) {
+ if (i == 1 && j > 1) sb.append("\n")
+ sb.append(if (q.getDataAt(x, y) == 0) " " else "X")
+ tv = PixelButton(this)
+ tv.isChecked = q.getUserMark(x, y) != 0
+ tv.setOnClickListener {
+ q.setUserMark(x, y, if (tv.isChecked) 0xFF else 0)
+ val columnCorrect = (grid.getChildAt(i) as? ClueView)?.check(q) ?: false
+ val rowCorrect = (grid.getChildAt(j*(grid.columnCount)) as? ClueView)
+ ?.check(q) ?: false
+ if (columnCorrect && rowCorrect) {
+ checkVictory()
+ } else {
+ label.visibility = GONE
+ }
+ }
+ } else if (i == j) { // 0,0
+ tv = View(this)
+ tv.visibility = GONE
+ } else {
+ tv = ClueView(this)
+ if (j == 0) {
+ tv.textRotation = 90f
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ params.height /= 2
+ tv.showText = false
+ } else {
+ params.height = (96 * dp).toInt()
+ }
+ if (x >= 0) {
+ tv.setColumn(q, x)
+ }
+ }
+ if (i == 0) {
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ params.width /= 2
+ tv.showText = false
+ } else {
+ params.width = (96 * dp).toInt()
+ }
+ if (y >= 0) {
+ tv.setRow(q, y)
+ }
+ }
+ }
+ grid.addView(tv, params)
+ }
+ }
+
+ Log.v(TAG, "icon: \n" + sb)
+ }
+}
+
+class PixelButton(context: Context) : CompoundButton(context) {
+ init {
+ setBackgroundResource(R.drawable.pixel_bg)
+ isClickable = true
+ isEnabled = true
+ }
+}
+
+class ClueView(context: Context) : View(context) {
+ var row: Int = -1
+ var column: Int = -1
+ var textRotation: Float = 0f
+ var text: CharSequence = ""
+ var showText = true
+ val paint: TextPaint
+ val incorrectColor: Int
+ val correctColor: Int
+
+ init {
+ setBackgroundColor(0)
+ paint = TextPaint().also {
+ it.textSize = 14f * context.resources.displayMetrics.density
+ it.color = context.getColor(R.color.q_clue_text)
+ it.typeface = Typeface.DEFAULT_BOLD
+ it.textAlign = Paint.Align.CENTER
+ }
+ incorrectColor = context.getColor(R.color.q_clue_bg)
+ correctColor = context.getColor(R.color.q_clue_bg_correct)
+ }
+
+ fun setRow(q: Quare, row: Int): Boolean {
+ this.row = row
+ this.column = -1
+ this.textRotation = 0f
+ text = q.getRowClue(row).joinToString("-")
+ return check(q)
+ }
+ fun setColumn(q: Quare, column: Int): Boolean {
+ this.column = column
+ this.row = -1
+ this.textRotation = 90f
+ text = q.getColumnClue(column).joinToString("-")
+ return check(q)
+ }
+ fun check(q: Quare): Boolean {
+ val correct = q.check(column, row)
+ setBackgroundColor(if (correct) correctColor else incorrectColor)
+ return correct
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ if (!showText) return
+ canvas?.let {
+ val x = canvas.width / 2f
+ val y = canvas.height / 2f
+ var textWidth = canvas.width
+ if (textRotation != 0f) {
+ canvas.rotate(textRotation, x, y)
+ textWidth = canvas.height
+ }
+ val textLayout = StaticLayout.Builder.obtain(
+ text, 0, text.length, paint, textWidth).build()
+ canvas.translate(x, y - textLayout.height / 2)
+ textLayout.draw(canvas)
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 7f906f6..6d874ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -117,7 +117,7 @@
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device) != BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index de38e8a..b15ea98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -18,15 +18,11 @@
import android.content.Context;
import android.net.NetworkTemplate;
-import android.os.ParcelUuid;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Utils class for data usage
*/
@@ -38,7 +34,7 @@
*/
public static NetworkTemplate getMobileTemplate(Context context, int subId) {
final TelephonyManager telephonyManager = context.getSystemService(
- TelephonyManager.class);
+ TelephonyManager.class).createForSubscriptionId(subId);
final SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
final SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subId);
@@ -49,23 +45,8 @@
Log.i(TAG, "Subscription is not active: " + subId);
return mobileAll;
}
- final ParcelUuid groupUuid = info.getGroupUuid();
- if (groupUuid == null) {
- Log.i(TAG, "Subscription doesn't have valid group uuid: " + subId);
- return mobileAll;
- }
- // Otherwise merge other subscriberId to create new NetworkTemplate
- final List<SubscriptionInfo> groupInfos = subscriptionManager.getSubscriptionsInGroup(
- groupUuid);
- final List<String> mergedSubscriberIds = new ArrayList<>();
- for (SubscriptionInfo subInfo : groupInfos) {
- final String subscriberId = telephonyManager.getSubscriberId(
- subInfo.getSubscriptionId());
- if (subscriberId != null) {
- mergedSubscriberIds.add(subscriberId);
- }
- }
- return NetworkTemplate.normalize(mobileAll, mergedSubscriberIds.toArray(new String[0]));
+ // Use old API to build networkTemplate
+ return NetworkTemplate.normalize(mobileAll, telephonyManager.getMergedSubscriberIds());
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index dc33cfe..821c0b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -44,7 +44,6 @@
public class DataUsageUtilsTest {
private static final int SUB_ID = 1;
- private static final int SUB_ID_2 = 2;
private static final String SUBSCRIBER_ID = "Test Subscriber";
private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2";
@@ -67,11 +66,11 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
- when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
- when(mInfo1.getSubscriptionId()).thenReturn(SUB_ID);
- when(mInfo2.getSubscriptionId()).thenReturn(SUB_ID_2);
+ when(mTelephonyManager.getMergedSubscriberIds()).thenReturn(
+ new String[]{SUBSCRIBER_ID, SUBSCRIBER_ID_2});
mInfos = new ArrayList<>();
mInfos.add(mInfo1);
@@ -89,17 +88,7 @@
}
@Test
- public void getMobileTemplate_groupUuidNull_returnMobileAll() {
- when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
- when(mInfo1.getGroupUuid()).thenReturn(null);
-
- final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
- }
-
- @Test
- public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
+ public void getMobileTemplate_infoExisted_returnMobileMerged() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 28d5402..52ec1f0 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@
*/
@ProvidesInterface(version = FalsingManager.VERSION)
public interface FalsingManager {
- int VERSION = 1;
+ int VERSION = 2;
void onSucccessfulUnlock();
@@ -103,4 +103,6 @@
void onTouchEvent(MotionEvent ev, int width, int height);
void dump(PrintWriter pw);
+
+ void cleanup();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index b1cba53..fee2c9b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -540,6 +540,9 @@
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
TransitionValues endValues) {
+ if (!sceneRoot.isShown()) {
+ return null;
+ }
final float cutoff = mCutoff;
final int startVisibility = View.INVISIBLE;
final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
@@ -552,6 +555,9 @@
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
TransitionValues endValues) {
+ if (!sceneRoot.isShown()) {
+ return null;
+ }
final float cutoff = 1f - mCutoff;
final int startVisibility = View.VISIBLE;
final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index e219e24..af4e61b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -89,6 +89,7 @@
private final HashMap<View, PendingIntent> mClickActions;
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
+ private final LayoutTransition mLayoutTransition;
private Uri mKeyguardSliceUri;
@VisibleForTesting
TextView mTitle;
@@ -126,16 +127,16 @@
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
- LayoutTransition transition = new LayoutTransition();
- transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
- transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
- transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
- transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
- transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
- transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
- transition.setAnimateParentHierarchy(false);
- setLayoutTransition(transition);
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
+ mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
+ mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
+ mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ mLayoutTransition.setInterpolator(LayoutTransition.APPEARING,
+ Interpolators.FAST_OUT_SLOW_IN);
+ mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
+ mLayoutTransition.setAnimateParentHierarchy(false);
}
@Override
@@ -174,6 +175,12 @@
mConfigurationController.removeCallback(this);
}
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ setLayoutTransition(isVisible ? mLayoutTransition : null);
+ }
+
/**
* Returns whether the current visible slice has a title/header.
*/
@@ -419,6 +426,7 @@
* their desired positions.
*/
private final Animation.AnimationListener mKeepAwakeListener;
+ private LayoutTransition mLayoutTransition;
private float mDarkAmount;
public Row(Context context) {
@@ -440,33 +448,41 @@
@Override
protected void onFinishInflate() {
- LayoutTransition transition = new LayoutTransition();
- transition.setDuration(DEFAULT_ANIM_DURATION);
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.setDuration(DEFAULT_ANIM_DURATION);
PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1);
ObjectAnimator changeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object) null,
left, right);
- transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator);
- transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator);
- transition.setInterpolator(LayoutTransition.CHANGE_APPEARING,
+ mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator);
+ mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator);
+ mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_APPEARING,
Interpolators.ACCELERATE_DECELERATE);
- transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
+ mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
Interpolators.ACCELERATE_DECELERATE);
- transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION);
- transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION);
+ mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_APPEARING,
+ DEFAULT_ANIM_DURATION);
+ mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING,
+ DEFAULT_ANIM_DURATION);
ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
- transition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
- transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN);
+ mLayoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
+ mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN);
ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
- transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
- transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
- transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
+ mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING,
+ Interpolators.ALPHA_OUT);
+ mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
+ mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
- transition.setAnimateParentHierarchy(false);
- setLayoutTransition(transition);
+ mLayoutTransition.setAnimateParentHierarchy(false);
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ setLayoutTransition(isVisible ? mLayoutTransition : null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 9a0e9fc..bd91333 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -46,6 +46,8 @@
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
private static final int DELAY_FINISH_RENDERING = 1000;
+ private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
+ private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
private HandlerThread mWorker;
@Override
@@ -80,7 +82,10 @@
private StatusBarStateController mController;
private final Runnable mFinishRenderingTask = this::finishRendering;
private final boolean mNeedTransition;
+ private final Object mMonitor = new Object();
private boolean mNeedRedraw;
+ // This variable can only be accessed in synchronized block.
+ private boolean mWaitingForRendering;
GLEngine(Context context) {
mNeedTransition = ActivityManager.isHighEndGfx()
@@ -119,9 +124,30 @@
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
- long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
+ if (!mNeedTransition) return;
mWorker.getThreadHandler().post(
- () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
+ () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
+ if (inAmbientMode && animationDuration == 0) {
+ // This means that we are transiting from home to aod, to avoid
+ // race condition between window visibility and transition,
+ // we don't return until the transition is finished. See b/136643341.
+ waitForBackgroundRendering();
+ }
+ }
+
+ private void waitForBackgroundRendering() {
+ synchronized (mMonitor) {
+ try {
+ mWaitingForRendering = true;
+ for (int patience = 1; mWaitingForRendering; patience++) {
+ mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
+ mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
+ }
+ } catch (InterruptedException ex) {
+ } finally {
+ mWaitingForRendering = false;
+ }
+ }
}
@Override
@@ -178,7 +204,8 @@
@Override
public void preRender() {
- mWorker.getThreadHandler().post(this::preRenderInternal);
+ // This method should only be invoked from worker thread.
+ preRenderInternal();
}
private void preRenderInternal() {
@@ -212,7 +239,8 @@
@Override
public void requestRender() {
- mWorker.getThreadHandler().post(this::requestRenderInternal);
+ // This method should only be invoked from worker thread.
+ requestRenderInternal();
}
private void requestRenderInternal() {
@@ -234,7 +262,21 @@
@Override
public void postRender() {
- mWorker.getThreadHandler().post(this::scheduleFinishRendering);
+ // This method should only be invoked from worker thread.
+ notifyWaitingThread();
+ scheduleFinishRendering();
+ }
+
+ private void notifyWaitingThread() {
+ synchronized (mMonitor) {
+ if (mWaitingForRendering) {
+ try {
+ mWaitingForRendering = false;
+ mMonitor.notify();
+ } catch (IllegalMonitorStateException ex) {
+ }
+ }
+ }
}
private void cancelFinishRenderingTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index 6fb6467..382c5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -168,6 +168,7 @@
.append("enabled=").append(isEnabled() ? 1 : 0)
.append(" mScreenOn=").append(mScreenOn ? 1 : 0)
.append(" mState=").append(StatusBarState.toShortString(mState))
+ .append(" mShowingAod=").append(mShowingAod ? 1 : 0)
.toString()
);
}
@@ -550,6 +551,14 @@
pw.println();
}
+ @Override
+ public void cleanup() {
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStatusBarStateListener);
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mKeyguardUpdateCallback);
+ }
+
public Uri reportRejectedTouch() {
if (mDataCollector.isEnabled()) {
return mDataCollector.reportRejectedTouch();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 3cc8ec9a..eb4edcc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -65,6 +65,7 @@
public void onPluginConnected(FalsingPlugin plugin, Context context) {
FalsingManager pluginFalsingManager = plugin.getFalsingManager(context);
if (pluginFalsingManager != null) {
+ mInternalFalsingManager.cleanup();
mInternalFalsingManager = pluginFalsingManager;
}
}
@@ -91,7 +92,10 @@
@VisibleForTesting
public void setupFalsingManager(Context context) {
boolean brightlineEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, false);
+ DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true);
+ if (mInternalFalsingManager != null) {
+ mInternalFalsingManager.cleanup();
+ }
if (!brightlineEnabled) {
mInternalFalsingManager = new FalsingManagerImpl(context);
} else {
@@ -290,4 +294,9 @@
public void dump(PrintWriter pw) {
mInternalFalsingManager.dump(pw);
}
+
+ @Override
+ public void cleanup() {
+ mInternalFalsingManager.cleanup();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 19c46e4..fbbba57 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -44,6 +44,8 @@
private final SensorManager mSensorManager;
private final FalsingDataProvider mDataProvider;
private boolean mSessionStarted;
+ private boolean mShowingAod;
+ private boolean mScreenOn;
private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor();
@@ -98,10 +100,12 @@
}
private void sessionStart() {
- logDebug("Starting Session");
- mSessionStarted = true;
- registerSensors();
- mClassifiers.forEach(FalsingClassifier::onSessionStarted);
+ if (!mSessionStarted && !mShowingAod && mScreenOn) {
+ logDebug("Starting Session");
+ mSessionStarted = true;
+ registerSensors();
+ mClassifiers.forEach(FalsingClassifier::onSessionStarted);
+ }
}
private void sessionEnd() {
@@ -157,6 +161,7 @@
@Override
public void onSucccessfulUnlock() {
+ sessionEnd();
}
@Override
@@ -165,6 +170,7 @@
@Override
public void setShowingAod(boolean showingAod) {
+ mShowingAod = showingAod;
if (showingAod) {
sessionEnd();
} else {
@@ -249,7 +255,7 @@
@Override
public void onScreenOnFromTouch() {
- sessionStart();
+ onScreenTurningOn();
}
@Override
@@ -271,11 +277,13 @@
@Override
public void onScreenTurningOn() {
+ mScreenOn = true;
sessionStart();
}
@Override
public void onScreenOff() {
+ mScreenOn = false;
sessionEnd();
}
@@ -309,6 +317,11 @@
public void dump(PrintWriter printWriter) {
}
+ @Override
+ public void cleanup() {
+ unregisterSensors();
+ }
+
static void logDebug(String msg) {
logDebug(msg, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
index 730907e..cc66454 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
@@ -16,9 +16,13 @@
package com.android.systemui.classifier.brightline;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import android.provider.DeviceConfig;
+
/**
* False on swipes that are too close to 45 degrees.
*
@@ -35,8 +39,20 @@
private static final float ONE_HUNDRED_EIGHTY_DEG = (float) (Math.PI);
private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
+ private final float mHorizontalAngleRange;
+ private final float mVerticalAngleRange;
+
DiagonalClassifier(FalsingDataProvider dataProvider) {
super(dataProvider);
+
+ mHorizontalAngleRange = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE,
+ HORIZONTAL_ANGLE_RANGE);
+ mVerticalAngleRange = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE,
+ VERTICAL_ANGLE_RANGE);
}
@Override
@@ -52,11 +68,11 @@
return false;
}
- float minAngle = DIAGONAL - HORIZONTAL_ANGLE_RANGE;
- float maxAngle = DIAGONAL + HORIZONTAL_ANGLE_RANGE;
+ float minAngle = DIAGONAL - mHorizontalAngleRange;
+ float maxAngle = DIAGONAL + mHorizontalAngleRange;
if (isVertical()) {
- minAngle = DIAGONAL - VERTICAL_ANGLE_RANGE;
- maxAngle = DIAGONAL + VERTICAL_ANGLE_RANGE;
+ minAngle = DIAGONAL - mVerticalAngleRange;
+ maxAngle = DIAGONAL + mVerticalAngleRange;
}
return angleBetween(angle, minAngle, maxAngle)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
index 005ee12..a6a617d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
@@ -16,6 +16,14 @@
package com.android.systemui.classifier.brightline;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
+
+import android.provider.DeviceConfig;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -31,12 +39,13 @@
private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
private static final float VELOCITY_TO_DISTANCE = 80f;
- private static final float SCREEN_FRACTION_MIN_DISTANCE = 0.8f;
+ private static final float SCREEN_FRACTION_MAX_DISTANCE = 0.8f;
private final float mVerticalFlingThresholdPx;
private final float mHorizontalFlingThresholdPx;
private final float mVerticalSwipeThresholdPx;
private final float mHorizontalSwipeThresholdPx;
+ private final float mVelocityToDistanceMultiplier;
private boolean mDistanceDirty;
private DistanceVectors mCachedDistance;
@@ -44,18 +53,48 @@
DistanceClassifier(FalsingDataProvider dataProvider) {
super(dataProvider);
+ mVelocityToDistanceMultiplier = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE,
+ VELOCITY_TO_DISTANCE);
+
+ float horizontalFlingThresholdIn = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN,
+ HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN);
+
+ float verticalFlingThresholdIn = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN,
+ VERTICAL_FLING_THRESHOLD_DISTANCE_IN);
+
+ float horizontalSwipeThresholdIn = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN,
+ HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN);
+
+ float verticalSwipeThresholdIn = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN,
+ VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN);
+
+ float screenFractionMaxDistance = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE,
+ SCREEN_FRACTION_MAX_DISTANCE);
+
mHorizontalFlingThresholdPx = Math
- .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE,
- HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN * getXdpi());
+ .min(getWidthPixels() * screenFractionMaxDistance,
+ horizontalFlingThresholdIn * getXdpi());
mVerticalFlingThresholdPx = Math
- .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE,
- VERTICAL_FLING_THRESHOLD_DISTANCE_IN * getYdpi());
+ .min(getHeightPixels() * screenFractionMaxDistance,
+ verticalFlingThresholdIn * getYdpi());
mHorizontalSwipeThresholdPx = Math
- .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE,
- HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN * getXdpi());
+ .min(getWidthPixels() * screenFractionMaxDistance,
+ horizontalSwipeThresholdIn * getXdpi());
mVerticalSwipeThresholdPx = Math
- .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE,
- VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN * getYdpi());
+ .min(getHeightPixels() * screenFractionMaxDistance,
+ verticalSwipeThresholdIn * getYdpi());
mDistanceDirty = true;
}
@@ -139,18 +178,18 @@
}
boolean getPassedFlingThreshold() {
- float dX = this.mDx + this.mVx * VELOCITY_TO_DISTANCE;
- float dY = this.mDy + this.mVy * VELOCITY_TO_DISTANCE;
+ float dX = this.mDx + this.mVx * mVelocityToDistanceMultiplier;
+ float dY = this.mDy + this.mVy * mVelocityToDistanceMultiplier;
if (isHorizontal()) {
logDebug("Horizontal swipe and fling distance: " + this.mDx + ", "
- + this.mVx * VELOCITY_TO_DISTANCE);
+ + this.mVx * mVelocityToDistanceMultiplier);
logDebug("Threshold: " + mHorizontalFlingThresholdPx);
return Math.abs(dX) >= mHorizontalFlingThresholdPx;
}
logDebug("Vertical swipe and fling distance: " + this.mDy + ", "
- + this.mVy * VELOCITY_TO_DISTANCE);
+ + this.mVy * mVelocityToDistanceMultiplier);
logDebug("Threshold: " + mVerticalFlingThresholdPx);
return Math.abs(dY) >= mVerticalFlingThresholdPx;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index 94a8ac85..2644bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -16,10 +16,12 @@
package com.android.systemui.classifier.brightline;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
+import android.provider.DeviceConfig;
import android.view.MotionEvent;
@@ -31,8 +33,9 @@
*/
class ProximityClassifier extends FalsingClassifier {
- private static final double PERCENT_COVERED_THRESHOLD = 0.1;
+ private static final float PERCENT_COVERED_THRESHOLD = 0.1f;
private final DistanceClassifier mDistanceClassifier;
+ private final float mPercentCoveredThreshold;
private boolean mNear;
private long mGestureStartTimeNs;
@@ -44,6 +47,11 @@
FalsingDataProvider dataProvider) {
super(dataProvider);
this.mDistanceClassifier = distanceClassifier;
+
+ mPercentCoveredThreshold = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD,
+ PERCENT_COVERED_THRESHOLD);
}
@Override
@@ -107,7 +115,7 @@
logInfo("Percent of gesture in proximity: " + mPercentNear);
- if (mPercentNear > PERCENT_COVERED_THRESHOLD) {
+ if (mPercentNear > mPercentCoveredThreshold) {
return !mDistanceClassifier.isLongSwipe();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index a62574f..c58b7db 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -16,7 +16,13 @@
package com.android.systemui.classifier.brightline;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
+
import android.graphics.Point;
+import android.provider.DeviceConfig;
import android.view.MotionEvent;
import java.util.ArrayList;
@@ -37,8 +43,34 @@
private static final float MAX_X_SECONDARY_DEVIANCE = .3f;
private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
+ private final float mMaxXPrimaryDeviance;
+ private final float mMaxYPrimaryDeviance;
+ private final float mMaxXSecondaryDeviance;
+ private final float mMaxYSecondaryDeviance;
+
ZigZagClassifier(FalsingDataProvider dataProvider) {
super(dataProvider);
+
+ mMaxXPrimaryDeviance = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE,
+ MAX_X_PRIMARY_DEVIANCE);
+
+ mMaxYPrimaryDeviance = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE,
+ MAX_Y_PRIMARY_DEVIANCE);
+
+ mMaxXSecondaryDeviance = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE,
+ MAX_X_SECONDARY_DEVIANCE);
+
+ mMaxYSecondaryDeviance = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE,
+ MAX_Y_SECONDARY_DEVIANCE);
+
}
@Override
@@ -98,11 +130,11 @@
float maxXDeviance;
float maxYDeviance;
if (actualDx > actualDy) {
- maxXDeviance = MAX_X_PRIMARY_DEVIANCE * totalDistanceIn * getXdpi();
- maxYDeviance = MAX_Y_SECONDARY_DEVIANCE * totalDistanceIn * getYdpi();
+ maxXDeviance = mMaxXPrimaryDeviance * totalDistanceIn * getXdpi();
+ maxYDeviance = mMaxYSecondaryDeviance * totalDistanceIn * getYdpi();
} else {
- maxXDeviance = MAX_X_SECONDARY_DEVIANCE * totalDistanceIn * getXdpi();
- maxYDeviance = MAX_Y_PRIMARY_DEVIANCE * totalDistanceIn * getYdpi();
+ maxXDeviance = mMaxXSecondaryDeviance * totalDistanceIn * getXdpi();
+ maxYDeviance = mMaxYPrimaryDeviance * totalDistanceIn * getYdpi();
}
logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 8694d2a..9c2adb3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -30,6 +30,7 @@
import com.android.systemui.SystemUIApplication;
import com.android.systemui.classifier.FalsingManagerFactory;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -70,7 +71,7 @@
new DozeScreenState(wrappedService, handler, params, wakeLock),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
handler),
- new DozeWallpaperState(context),
+ new DozeWallpaperState(context, getBiometricUnlockController(dozeService)),
new DozeDockHandler(context, machine, host, config, handler, dockManager)
});
@@ -107,4 +108,10 @@
final SystemUIApplication app = (SystemUIApplication) appCandidate;
return app.getComponent(DozeHost.class);
}
+
+ public static BiometricUnlockController getBiometricUnlockController(DozeService service) {
+ Application appCandidate = service.getApplication();
+ final SystemUIApplication app = (SystemUIApplication) appCandidate;
+ return app.getComponent(BiometricUnlockController.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 5f52486..a882309 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -43,6 +43,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.R;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AlarmTimeout;
@@ -255,12 +256,21 @@
long mLastNear;
final AlarmTimeout mCooldownTimer;
final AlwaysOnDisplayPolicy mPolicy;
-
+ final Sensor mSensor;
public ProxSensor(AlwaysOnDisplayPolicy policy) {
mPolicy = policy;
mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
"prox_cooldown", mHandler);
+
+ // The default prox sensor can be noisy, so let's use a prox gated brightness sensor
+ // if available.
+ Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
+ mContext.getString(R.string.doze_brightness_sensor_type));
+ if (sensor == null) {
+ sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+ mSensor = sensor;
}
void setRequested(boolean requested) {
@@ -324,8 +334,9 @@
@Override
public String toString() {
- return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s}",
- mRegistered, mRequested, mCooldownTimer.isScheduled(), mCurrentlyFar);
+ return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s,"
+ + " sensor=%s}", mRegistered, mRequested, mCooldownTimer.isScheduled(),
+ mCurrentlyFar, mSensor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 1b3cd88..35c8b74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -24,6 +24,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
@@ -38,18 +39,22 @@
private final IWallpaperManager mWallpaperManagerService;
private final DozeParameters mDozeParameters;
+ private final BiometricUnlockController mBiometricUnlockController;
private boolean mIsAmbientMode;
- public DozeWallpaperState(Context context) {
+ public DozeWallpaperState(Context context,
+ BiometricUnlockController biometricUnlockController) {
this(IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE)),
+ biometricUnlockController,
DozeParameters.getInstance(context));
}
@VisibleForTesting
DozeWallpaperState(IWallpaperManager wallpaperManagerService,
- DozeParameters parameters) {
+ BiometricUnlockController biometricUnlockController, DozeParameters parameters) {
mWallpaperManagerService = wallpaperManagerService;
+ mBiometricUnlockController = biometricUnlockController;
mDozeParameters = parameters;
}
@@ -76,7 +81,9 @@
} else {
boolean wakingUpFromPulse = oldState == DozeMachine.State.DOZE_PULSING
&& newState == DozeMachine.State.FINISH;
- animated = !mDozeParameters.getDisplayNeedsBlanking() || wakingUpFromPulse;
+ boolean fastDisplay = !mDozeParameters.getDisplayNeedsBlanking();
+ animated = (fastDisplay && !mBiometricUnlockController.unlockedByWakeAndUnlock())
+ || wakingUpFromPulse;
}
if (isAmbientMode != mIsAmbientMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index 6a1f24a..b154e66 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -65,7 +65,7 @@
@Override
public void onAnimationStart(Animator animation) {
if (mRevealListener != null) {
- mRevealListener.onRevealStart();
+ mRevealListener.onRevealStart(true /* animate */);
}
}
});
@@ -73,7 +73,7 @@
private void animate() {
mAnimator.cancel();
- mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
+ mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
mAnimator.start();
}
@@ -84,7 +84,16 @@
void updateAwake(boolean awake, long duration) {
mAwake = awake;
mAnimator.setDuration(duration);
- animate();
+ if (duration == 0) {
+ // We are transiting from home to aod or aod to home directly,
+ // we don't need to do transition in these cases.
+ mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL;
+ mRevealListener.onRevealStart(false /* animate */);
+ mRevealListener.onRevealStateChanged();
+ mRevealListener.onRevealEnd();
+ } else {
+ animate();
+ }
}
/**
@@ -100,7 +109,7 @@
/**
* Called back while reveal starts.
*/
- void onRevealStart();
+ void onRevealStart(boolean animate);
/**
* Called back while reveal ends.
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 93d8dd6..7b22a49 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -24,6 +24,7 @@
import android.app.WallpaperManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.Log;
@@ -70,7 +71,14 @@
DisplayInfo displayInfo = new DisplayInfo();
WindowManager wm = context.getSystemService(WindowManager.class);
wm.getDefaultDisplay().getDisplayInfo(displayInfo);
- mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ // We only do transition in portrait currently, b/137962047.
+ int orientation = context.getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ } else {
+ mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth);
+ }
mProxy = proxy;
mProgram = new ImageGLProgram(context);
@@ -179,20 +187,24 @@
}
@Override
- public void onRevealStart() {
- mScissorMode = true;
- // Use current display area of texture.
- mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
+ public void onRevealStart(boolean animate) {
+ if (animate) {
+ mScissorMode = true;
+ // Use current display area of texture.
+ mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
+ }
mProxy.preRender();
}
@Override
public void onRevealEnd() {
- mScissorMode = false;
- // reset texture coordinates to use full texture.
- mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
- // We need draw full texture back before finishing render.
- mProxy.requestRender();
+ if (mScissorMode) {
+ mScissorMode = false;
+ // reset texture coordinates to use full texture.
+ mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
+ // We need draw full texture back before finishing render.
+ mProxy.requestRender();
+ }
mProxy.postRender();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 2047797..0687b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -52,6 +52,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -125,6 +126,7 @@
private CharSequence mMediaTitle;
private CharSequence mMediaArtist;
protected boolean mDozing;
+ private int mStatusBarState;
private boolean mMediaIsVisible;
/**
@@ -227,7 +229,10 @@
}
protected boolean needsMediaLocked() {
- return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && mDozing;
+ // Show header if music is playing and the status bar is in the shade state. This way, an
+ // animation isn't necessary when pressing power and transitioning to AOD.
+ boolean keepWhenShade = mStatusBarState == StatusBarState.SHADE && mMediaIsVisible;
+ return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenShade);
}
protected void addMediaLocked(ListBuilder listBuilder) {
@@ -454,7 +459,7 @@
synchronized (this) {
boolean nextVisible = !mMediaInvisibleStates.contains(state);
mHandler.removeCallbacksAndMessages(mMediaToken);
- if (mMediaIsVisible && !nextVisible) {
+ if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) {
// We need to delay this event for a few millis when stopping to avoid jank in the
// animation. The media app might not send its update when buffering, and the slice
// would end up without a header for 0.5 second.
@@ -511,5 +516,14 @@
@Override
public void onStateChanged(int newState) {
+ final boolean notify;
+ synchronized (this) {
+ boolean needsMedia = needsMediaLocked();
+ mStatusBarState = newState;
+ notify = needsMedia != needsMediaLocked();
+ }
+ if (notify) {
+ notifyChange();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 0fe5f8a..4cc5b21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -15,7 +15,6 @@
package com.android.systemui.statusbar;
import android.content.pm.UserInfo;
-import android.service.notification.StatusBarNotification;
import android.util.SparseArray;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -58,7 +57,7 @@
boolean shouldHideNotifications(int userId);
boolean shouldHideNotifications(String key);
- boolean shouldShowOnKeyguard(StatusBarNotification sbn);
+ boolean shouldShowOnKeyguard(NotificationEntry entry);
boolean isAnyProfilePublicMode();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 4ea1ed5..e08a5ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -33,7 +33,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -302,7 +301,7 @@
Notification.VISIBILITY_SECRET;
}
- public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+ public boolean shouldShowOnKeyguard(NotificationEntry entry) {
if (getEntryManager() == null) {
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return false;
@@ -310,10 +309,10 @@
boolean exceedsPriorityThreshold;
if (NotificationUtils.useNewInterruptionModel(mContext)
&& hideSilentNotificationsOnLockscreen()) {
- exceedsPriorityThreshold = getEntryManager().getNotificationData().isHighPriority(sbn);
+ exceedsPriorityThreshold = entry.isTopBucket();
} else {
exceedsPriorityThreshold =
- !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+ !getEntryManager().getNotificationData().isAmbient(entry.key);
}
return mShowLockscreenNotifications && exceedsPriorityThreshold;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 312ea47..165c64e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -512,7 +512,7 @@
float viewEnd = row.getTranslationY() + row.getActualHeight();
boolean isPinned = (row.isPinned() || row.isHeadsUpAnimatingAway())
&& !mAmbientState.isDozingAndNotPulsing(row);
- boolean shouldClipOwnTop = row.showingAmbientPulsing()
+ boolean shouldClipOwnTop = row.showingAmbientPulsing() && !mAmbientState.isFullyDark()
|| (mAmbientState.isPulseExpanding() && childIndex == 0);
if (viewEnd > notificationClipEnd && !shouldClipOwnTop
&& (mAmbientState.isShadeExpanded() || !isPinned)) {
@@ -752,8 +752,9 @@
iconState.scaleY = 1.0f;
iconState.hidden = false;
}
- if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
- || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
+ if ((row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
+ || row.getTranslationZ() > mAmbientState.getBaseZHeight())))
+ && !mAmbientState.isFullyDark()) {
iconState.hidden = true;
}
int backgroundColor = getBackgroundColorWithoutTint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 4049201..9c3ee96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -394,15 +394,13 @@
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.isRowRemoved();
- boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
- .notification);
+ boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
entry.notification);
- if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
- summary.notification)) {
+ if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
showOnKeyguard = true;
}
}
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 0d9f4e7..91d4707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -153,6 +153,7 @@
if (primary == null) {
setAnimationPending(false);
invokeCallback(iRemoteAnimationFinishedCallback);
+ mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 64b2f04..fb10084 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -25,7 +25,6 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
@@ -108,10 +107,19 @@
boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
&& isSystemNotification(nb);
- boolean isHeadsUp = a.getRow().isHeadsUp();
- if (isHeadsUp != b.getRow().isHeadsUp()) {
- return isHeadsUp ? -1 : 1;
- } else if (isHeadsUp) {
+
+ boolean aHeadsUp = a.getRow().isHeadsUp();
+ boolean bHeadsUp = b.getRow().isHeadsUp();
+
+ // HACK: This should really go elsewhere, but it's currently not straightforward to
+ // extract the comparison code and we're guaranteed to touch every element, so this is
+ // the best place to set the buckets for the moment.
+ a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority());
+ b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority());
+
+ if (aHeadsUp != bHeadsUp) {
+ return aHeadsUp ? -1 : 1;
+ } else if (aHeadsUp) {
// Provide consistent ranking with headsUpManager
return mHeadsUpManager.compare(a, b);
} else if (a.getRow().showingAmbientPulsing() != b.getRow().showingAmbientPulsing()) {
@@ -414,7 +422,14 @@
}
}
- Collections.sort(mSortedAndFiltered, mRankingComparator);
+ if (mSortedAndFiltered.size() == 1) {
+ // HACK: We need the comparator to run on all children in order to set the
+ // isHighPriority field. If there is only one child, then the comparison won't be run,
+ // so we have to trigger it manually. Get rid of this code as soon as possible.
+ mRankingComparator.compare(mSortedAndFiltered.get(0), mSortedAndFiltered.get(0));
+ } else {
+ Collections.sort(mSortedAndFiltered, mRankingComparator);
+ }
}
public void dump(PrintWriter pw, String indent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 92c261c..d157f06c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -173,6 +173,8 @@
*/
private boolean mHighPriority;
+ private boolean mIsTopBucket;
+
public NotificationEntry(StatusBarNotification n) {
this(n, null);
}
@@ -220,6 +222,18 @@
this.mHighPriority = highPriority;
}
+ /**
+ * @return True if the notif should appear in the "top" or "important" section of notifications
+ * (as opposed to the "bottom" or "silent" section). This is usually the same as
+ * {@link #isHighPriority()}, but there are certain exceptions, such as media notifs.
+ */
+ public boolean isTopBucket() {
+ return mIsTopBucket;
+ }
+ public void setIsTopBucket(boolean isTopBucket) {
+ mIsTopBucket = isTopBucket;
+ }
+
public boolean isBubble() {
return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 170a4d5..d119fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -133,7 +133,7 @@
if (child instanceof ExpandableNotificationRow
&& child.getVisibility() != View.GONE) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!row.getEntry().isHighPriority()) {
+ if (!row.getEntry().isTopBucket()) {
firstGentleNotifIndex = i;
mFirstGentleNotif = row;
break;
@@ -248,7 +248,7 @@
View child = mParent.getChildAt(i);
if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!row.getEntry().isHighPriority()) {
+ if (!row.getEntry().isTopBucket()) {
break;
} else {
lastChildBeforeGap = row;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 8fe3418..f7b79d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -88,6 +88,7 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.classifier.FalsingManagerFactory;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -506,6 +507,7 @@
* If the {@link NotificationShelf} should be visible when dark.
*/
private boolean mAnimateBottomOnLayout;
+ private int mPulseReason;
@Inject
public NotificationStackScrollLayout(
@@ -1355,7 +1357,8 @@
mIsClipped = clipped;
}
- if (!mAmbientPulseManager.hasNotifications() && mAmbientState.isFullyDark()) {
+ if ((!mPulsing || mPulseReason == DozeLog.PULSE_REASON_DOCKING)
+ && mAmbientState.isFullyDark()) {
setClipBounds(null);
} else if (mAmbientState.isDarkAtAll()) {
clipToOutline = true;
@@ -2516,12 +2519,20 @@
}
return;
}
- int minTopPosition = 0;
+ int minTopPosition;
NotificationSection lastSection = getLastVisibleSection();
if (mStatusBarState != StatusBarState.KEYGUARD) {
minTopPosition = (int) (mTopPadding + mStackTranslation);
} else if (lastSection == null) {
minTopPosition = mTopPadding;
+ } else {
+ // The first sections could be empty while there could still be elements in later
+ // sections. The position of these first few sections is determined by the position of
+ // the first visible section.
+ NotificationSection firstVisibleSection = getFirstVisibleSection();
+ firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */,
+ false /* shiftPulsingWithFirst */);
+ minTopPosition = firstVisibleSection.getBounds().top;
}
boolean shiftPulsingWithFirst = mAmbientPulseManager.getAllEntries().count() <= 1;
for (NotificationSection section : mSections) {
@@ -5161,6 +5172,7 @@
return;
}
mPulsing = pulsing;
+ updateClipping();
mAmbientState.setPulsing(pulsing);
mSwipeHelper.setPulsing(pulsing);
updateNotificationAnimationStates();
@@ -5170,6 +5182,11 @@
notifyHeightChangeListener(null, animated);
}
+ public void setPulseReason(int pulseReason) {
+ mPulseReason = pulseReason;
+ updateClipping();
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpanded(boolean qsExpanded) {
mQsExpanded = qsExpanded;
@@ -5768,7 +5785,7 @@
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
- beforeSpeedBump = row.getEntry().isHighPriority();
+ beforeSpeedBump = row.getEntry().isTopBucket();
} else {
beforeSpeedBump = !row.getEntry().ambient;
}
@@ -5826,9 +5843,9 @@
case ROWS_ALL:
return true;
case ROWS_HIGH_PRIORITY:
- return row.getEntry().isHighPriority();
+ return row.getEntry().isTopBucket();
case ROWS_GENTLE:
- return !row.getEntry().isHighPriority();
+ return !row.getEntry().isTopBucket();
default:
throw new IllegalArgumentException("Unknown selection: " + selection);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 60061c6..d9f8c88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -501,7 +501,8 @@
continue;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (!row.showingAmbientPulsing() || (i == 0 && ambientState.isPulseExpanding())) {
+ if (!row.showingAmbientPulsing() || ambientState.isFullyDark()
+ || (i == 0 && ambientState.isPulseExpanding())) {
continue;
}
ExpandableViewState viewState = row.getViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 4d4818d..eccd70b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -132,6 +132,7 @@
private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
+ private boolean mFadedAwayAfterWakeAndUnlock;
private final TunerService.Tunable mFaceDismissedKeyguardTunable = new TunerService.Tunable() {
@Override
@@ -360,6 +361,7 @@
@Override
public void onStartedGoingToSleep(int why) {
resetMode();
+ mFadedAwayAfterWakeAndUnlock = false;
mPendingAuthenticatedUserId = -1;
mPendingAuthenticatedBioSourceType = null;
}
@@ -454,6 +456,9 @@
}
public void finishKeyguardFadingAway() {
+ if (isWakeAndUnlock()) {
+ mFadedAwayAfterWakeAndUnlock = true;
+ }
resetMode();
}
@@ -504,6 +509,14 @@
}
/**
+ * Successful authentication with fingerprint, face, or iris that wakes up the device.
+ * This will return {@code true} even after the keyguard fades away.
+ */
+ public boolean unlockedByWakeAndUnlock() {
+ return isWakeAndUnlock() || mFadedAwayAfterWakeAndUnlock;
+ }
+
+ /**
* Successful authentication with fingerprint, face, or iris when the screen was either
* on or off.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index a0cda69..595c1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.Region;
import android.util.Log;
import android.util.Pools;
import android.view.DisplayCutout;
@@ -76,7 +77,7 @@
private int[] mTmpTwoArray = new int[2];
private boolean mHeadsUpGoingAway;
private int mStatusBarState;
- private Rect mTouchableRegion = new Rect();
+ private Region mTouchableRegion = new Region();
private AnimationStateHandler mAnimationStateHandler;
@@ -299,12 +300,16 @@
info.touchableRegion.set(calculateTouchableRegion());
}
- public Rect calculateTouchableRegion() {
- if (!hasPinnedHeadsUp()) {
+ public Region calculateTouchableRegion() {
+ NotificationEntry topEntry = getTopEntry();
+ // This call could be made in an inconsistent state while the pinnedMode hasn't been
+ // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's
+ // therefore also check if the topEntry is null.
+ if (!hasPinnedHeadsUp() || topEntry == null) {
mTouchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
updateRegionForNotch(mTouchableRegion);
+
} else {
- NotificationEntry topEntry = getTopEntry();
if (topEntry.isChildInGroup()) {
final NotificationEntry groupSummary =
mGroupManager.getGroupSummary(topEntry.notification);
@@ -322,7 +327,7 @@
return mTouchableRegion;
}
- private void updateRegionForNotch(Rect region) {
+ private void updateRegionForNotch(Region region) {
DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout();
if (cutout == null) {
return;
@@ -390,6 +395,8 @@
super.dumpInternal(fd, pw, args);
pw.print(" mBarState=");
pw.println(mStatusBarState);
+ pw.print(" mTouchableRegion=");
+ pw.println(mTouchableRegion);
}
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 1027046..3665dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,6 +38,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.PowerManager;
import android.util.AttributeSet;
import android.util.Log;
@@ -620,9 +621,10 @@
private Rect calculateGestureExclusionRect() {
Rect exclusionRect = null;
- if (isFullyCollapsed()) {
+ Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
+ if (isFullyCollapsed() && touchableRegion != null) {
// Note: The heads up manager also calculates the non-pinned touchable region
- exclusionRect = mHeadsUpManager.calculateTouchableRegion();
+ exclusionRect = touchableRegion.getBounds();
}
return exclusionRect != null
? exclusionRect
@@ -732,8 +734,7 @@
if (suppressedSummary) {
continue;
}
- if (!mLockscreenUserManager.shouldShowOnKeyguard(
- row.getStatusBarNotification())) {
+ if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
continue;
}
if (row.isRemoved()) {
@@ -3133,6 +3134,10 @@
mAnimateNextPositionUpdate = true;
}
+ public void setPulseReason(int reason) {
+ mNotificationStackScroller.setPulseReason(reason);
+ }
+
/**
* Panel and QS expansion callbacks.
*/
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 1aa8bd7..2c305df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -482,9 +482,12 @@
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ final boolean imageWallpaperInAmbient =
+ !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
// If WallpaperInfo is null, it must be ImageWallpaper.
final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info == null || info.supportsAmbientMode());
+ && ((info == null && imageWallpaperInAmbient)
+ || (info != null && info.supportsAmbientMode()));
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -1213,6 +1216,7 @@
mDozeScrimController, keyguardViewMediator,
mScrimController, this, UnlockMethodCache.getInstance(mContext),
new Handler(), mKeyguardUpdateMonitor, Dependency.get(TunerService.class));
+ putComponent(BiometricUnlockController.class, mBiometricUnlockController);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
mStatusBarWindow.findViewById(R.id.lock_icon_container));
@@ -3924,6 +3928,7 @@
// execute the transition. The pulse callback will then be invoked when the scrims
// are black, indicating that StatusBar is ready to present the rest of the UI.
mPulsing = true;
+ mNotificationPanel.setPulseReason(reason);
mDozeScrimController.pulse(new PulseCallback() {
@Override
public void onPulseStarted() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index 329ef1c..7ea6493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -56,6 +56,9 @@
mHandler = new Handler(mTestableLooper.getLooper());
mDefaultConfigValue = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, false);
+ // In case it runs on a device where it's been set to true, set it to false by hand.
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 87ae85f..f07edf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -18,6 +18,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,6 +30,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
@@ -44,12 +46,14 @@
private DozeWallpaperState mDozeWallpaperState;
@Mock IWallpaperManager mIWallpaperManager;
+ @Mock BiometricUnlockController mBiometricUnlockController;
@Mock DozeParameters mDozeParameters;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters);
+ mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mBiometricUnlockController,
+ mDozeParameters);
}
@Test
@@ -102,6 +106,20 @@
}
@Test
+ public void testDoesNotAnimate_whenWakeAndUnlock() throws RemoteException {
+ // Pre-conditions
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mBiometricUnlockController.unlockedByWakeAndUnlock()).thenReturn(true);
+
+ mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
+ DozeMachine.State.DOZE_AOD);
+ clearInvocations(mIWallpaperManager);
+
+ mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
+ verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L));
+ }
+
+ @Test
public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException {
mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE,
DozeMachine.State.DOZE_REQUEST_PULSE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 4419114..a83c4b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.wakelock.SettableWakeLock;
@@ -177,6 +178,7 @@
@Test
public void onMetadataChanged_updatesSlice() {
+ mProvider.onStateChanged(StatusBarState.KEYGUARD);
mProvider.onDozingChanged(true);
reset(mContentResolver);
mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
@@ -190,6 +192,7 @@
@Test
public void onDozingChanged_updatesSliceIfMedia() {
+ mProvider.onStateChanged(StatusBarState.KEYGUARD);
mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
reset(mContentResolver);
// Show media when dozing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 49a263a..57dd8c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -38,7 +38,6 @@
import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -48,6 +47,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -166,7 +166,7 @@
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
when(mNotificationData.isHighPriority(any())).thenReturn(false);
- assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+ assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
}
@Test
@@ -179,7 +179,7 @@
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
when(mNotificationData.isHighPriority(any())).thenReturn(false);
- assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+ assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
}
private class TestNotificationLockscreenUserManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 6e0ddbf..f629757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -23,6 +23,7 @@
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
@@ -62,6 +63,8 @@
import android.testing.TestableLooper.RunWithLooper;
import android.util.ArraySet;
+import androidx.test.filters.SmallTest;
+
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
@@ -84,8 +87,6 @@
import java.util.List;
import java.util.Map;
-import androidx.test.filters.SmallTest;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -113,6 +114,7 @@
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
when(mMockStatusBarNotification.cloneLight()).thenReturn(mMockStatusBarNotification);
+ when(mMockStatusBarNotification.getKey()).thenReturn("mock_key");
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
@@ -231,6 +233,7 @@
Notification n = mMockStatusBarNotification.getNotification();
n.flags = Notification.FLAG_FOREGROUND_SERVICE;
NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+ entry.setRow(mRow);
mNotificationData.add(entry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
@@ -249,6 +252,7 @@
n = nb.build();
when(mMockStatusBarNotification.getNotification()).thenReturn(n);
NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+ entry.setRow(mRow);
mNotificationData.add(entry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
@@ -262,6 +266,7 @@
public void testIsExemptFromDndVisualSuppression_system() {
initStatusBarNotification(false);
NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+ entry.setRow(mRow);
entry.mIsSystemNotification = true;
mNotificationData.add(entry);
Bundle override = new Bundle();
@@ -276,6 +281,7 @@
public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
initStatusBarNotification(false);
NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
+ entry.setRow(mRow);
entry.mIsSystemNotification = true;
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT);
@@ -528,6 +534,62 @@
assertEquals(-1, mNotificationData.mRankingComparator.compare(a, b));
}
+ @Test
+ public void testSort_properlySetsIsTopBucket() {
+
+ Notification notification = new Notification.Builder(mContext, "test")
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ "pkg",
+ "pkg",
+ 0,
+ "tag",
+ 0,
+ 0,
+ notification,
+ mContext.getUser(),
+ "",
+ 0);
+
+ Bundle override = new Bundle();
+ override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT);
+ mNotificationData.rankingOverrides.put(sbn.getKey(), override);
+
+ NotificationEntry entry = new NotificationEntry(sbn);
+ entry.setRow(mRow);
+ mNotificationData.add(entry);
+
+ assertTrue(entry.isTopBucket());
+ }
+
+ @Test
+ public void testSort_properlySetsIsNotTopBucket() {
+ Notification notification = new Notification.Builder(mContext, "test")
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ "pkg",
+ "pkg",
+ 0,
+ "tag",
+ 0,
+ 0,
+ notification,
+ mContext.getUser(),
+ "",
+ 0);
+
+ Bundle override = new Bundle();
+ override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
+ mNotificationData.rankingOverrides.put(sbn.getKey(), override);
+
+ NotificationEntry entry = new NotificationEntry(sbn);
+ entry.setRow(mRow);
+
+ mNotificationData.add(entry);
+
+ assertFalse(entry.isTopBucket());
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 73abda9..59d0f91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -263,6 +263,8 @@
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
when(notifRow.getEntry().isHighPriority())
.thenReturn(children[i] == ChildType.HIPRI);
+ when(notifRow.getEntry().isTopBucket())
+ .thenReturn(children[i] == ChildType.HIPRI);
when(notifRow.getParent()).thenReturn(mNssl);
child = notifRow;
break;
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 70569db..4639d75 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -191,7 +191,7 @@
if (!file.getFileDescriptor().valid()) {
throw new IllegalStateException("Invalid file descriptor");
}
- return ParcelFileDescriptor.dup(file.getFileDescriptor());
+ return new ParcelFileDescriptor(file.getFileDescriptor());
} catch (IOException ex) {
throw new IllegalStateException("Failed to get PFD from memory file", ex);
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index bc7da3f..30a3563 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -413,15 +413,15 @@
try {
synchronized (mLock) {
if (mNightMode != mode) {
- if (UserManager.get(getContext()).isPrimaryUser()) {
- SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME,
- Integer.toString(mode));
- }
-
// Only persist setting if not in car mode
if (!mCarModeEnabled) {
Secure.putIntForUser(getContext().getContentResolver(),
Secure.UI_NIGHT_MODE, mode, user);
+
+ if (UserManager.get(getContext()).isPrimaryUser()) {
+ SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME,
+ Integer.toString(mode));
+ }
}
mNightMode = mode;
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 5cbd237..4ef63c0 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -122,7 +122,7 @@
*/
public void markSlotDeleted(int slot) throws RuntimeException {
ensureSlotMapLoaded();
- if (mSlotMap.containsKey(slot) && mSlotMap.get(slot) != getMode()) {
+ if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
throw new RuntimeException("password slot " + slot + " cannot be deleted");
}
mSlotMap.remove(slot);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 6f28675..934511b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -296,22 +296,12 @@
*/
private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
final int userId, final int flags) {
- final List<OverlayInfo> ois = new ArrayList<>();
+ final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
+ userId);
- // Framework overlays added first because order matters when resolving a resource
- if (!"android".equals(targetPackageName)) {
- ois.addAll(mSettings.getOverlaysForTarget("android", userId));
- }
-
- // Then add the targeted, non-framework overlays which have higher priority
- ois.addAll(mSettings.getOverlaysForTarget(targetPackageName, userId));
-
- final List<String> enabledBaseCodePaths = new ArrayList<>(ois.size());
-
+ // Update the state for any overlay that targets this package.
boolean modified = false;
- final int n = ois.size();
- for (int i = 0; i < n; i++) {
- final OverlayInfo oi = ois.get(i);
+ for (final OverlayInfo oi : targetOverlays) {
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
userId);
if (overlayPackage == null) {
@@ -324,25 +314,39 @@
Slog.e(TAG, "failed to update settings", e);
modified |= mSettings.remove(oi.packageName, userId);
}
-
- if (oi.isEnabled() && overlayPackage.applicationInfo != null) {
- enabledBaseCodePaths.add(overlayPackage.applicationInfo.getBaseCodePath());
- }
}
}
if (!modified) {
+ // Update the overlay paths of the target within package manager if necessary.
+ final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size());
+
+ // Framework overlays are first in the overlay paths of a package within PackageManager.
+ for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) {
+ if (oi.isEnabled()) {
+ enabledOverlayPaths.add(oi.baseCodePath);
+ }
+ }
+
+ for (final OverlayInfo oi : targetOverlays) {
+ if (oi.isEnabled()) {
+ enabledOverlayPaths.add(oi.baseCodePath);
+ }
+ }
+
+ // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of
+ // resourceDirs if in the future resourceDirs contains APKs other than overlays
PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo;
String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs;
// If the lists aren't the same length, the enabled overlays have changed
- if (ArrayUtils.size(resourceDirs) != enabledBaseCodePaths.size()) {
+ if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) {
modified = true;
} else if (resourceDirs != null) {
// If any element isn't equal, an overlay or the order of overlays has changed
for (int index = 0; index < resourceDirs.length; index++) {
- if (!resourceDirs[index].equals(enabledBaseCodePaths.get(index))) {
+ if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) {
modified = true;
break;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 24a0c88..94d262b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -318,6 +318,7 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -11032,14 +11033,15 @@
final String realPkgName = request.realPkgName;
final List<String> changedAbiCodePath = result.changedAbiCodePath;
final PackageSetting pkgSetting;
+ if (request.pkgSetting != null && request.pkgSetting.sharedUser != null
+ && request.pkgSetting.sharedUser != result.pkgSetting.sharedUser) {
+ // shared user changed, remove from old shared user
+ request.pkgSetting.sharedUser.removePackage(request.pkgSetting);
+ }
if (result.existingSettingCopied) {
pkgSetting = request.pkgSetting;
pkgSetting.updateFrom(result.pkgSetting);
pkg.mExtras = pkgSetting;
- if (pkgSetting.sharedUser != null
- && pkgSetting.sharedUser.removePackage(result.pkgSetting)) {
- pkgSetting.sharedUser.addPackage(pkgSetting);
- }
} else {
pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
@@ -11049,6 +11051,9 @@
mTransferedPackages.add(originalPkgSetting.name);
}
}
+ if (pkgSetting.sharedUser != null) {
+ pkgSetting.sharedUser.addPackage(pkgSetting);
+ }
// TODO(toddke): Consider a method specifically for modifying the Package object
// post scan; or, moving this stuff out of the Package object since it has nothing
// to do with the package on disk.
@@ -16591,13 +16596,13 @@
&& compareSignatures(sharedUserSignatures,
pkg.mSigningDetails.signatures)
!= PackageManager.SIGNATURE_MATCH) {
- if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
// packages will likely break the device in unforeseen ways.
- // However,
- // we allow the device to boot anyway because, prior to P,
- // vendors were
- // not expecting the platform to crash in this situation.
+ // However, we allow the device to boot anyway because, prior to Q,
+ // vendors were not expecting the platform to crash in this
+ // situation.
+ // This WILL be a hard failure on any new API levels after Q.
throw new ReconcileFailure(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
@@ -21634,6 +21639,17 @@
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
+
+ final PermissionPolicyInternal permissionPolicyInternal =
+ LocalServices.getService(PermissionPolicyInternal.class);
+ permissionPolicyInternal.setOnInitializedCallback(userId -> {
+ // The SDK updated case is already handled when we run during the ctor.
+ synchronized (mPackages) {
+ mPermissionManager.updateAllPermissions(
+ StorageManager.UUID_PRIVATE_INTERNAL, false /*sdkUpdated*/,
+ mPackages.values(), mPermissionCallback);
+ }
+ });
}
// Watch for external volumes that come and go over time
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index f8c4f6b..4550446 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1170,6 +1170,11 @@
final int flags = mContext.getPackageManager().getPermissionFlags(
permission, pkg.packageName, user);
+ // If we are trying to grant as system fixed and already system fixed
+ // then the system can change the system fixed grant state.
+ final boolean changingGrantForSystemFixed = systemFixed
+ && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
+
// Certain flags imply that the permission's current state by the system or
// device/profile owner or the user. In these cases we do not want to clobber the
// current state.
@@ -1177,7 +1182,8 @@
// Unless the caller wants to override user choices. The override is
// to make sure we can grant the needed permission to the default
// sms and phone apps after the user chooses this in the UI.
- if (!isFixedOrUserSet(flags) || ignoreSystemPackage) {
+ if (!isFixedOrUserSet(flags) || ignoreSystemPackage
+ || changingGrantForSystemFixed) {
// Never clobber policy fixed permissions.
// We must allow the grant of a system-fixed permission because
// system-fixed is sticky, but the permission itself may be revoked.
@@ -1196,6 +1202,14 @@
PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);
}
+ // If the system tries to change a system fixed permission from one fixed
+ // state to another we need to drop the fixed flag to allow the grant.
+ if (changingGrantForSystemFixed) {
+ mContext.getPackageManager().updatePermissionFlags(permission,
+ pkg.packageName, flags,
+ flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user);
+ }
+
if (pm.checkPermission(permission, pkg.packageName)
!= PackageManager.PERMISSION_GRANTED) {
mContext.getPackageManager()
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d3e5df5..8884821 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -97,6 +97,7 @@
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
import libcore.util.EmptyArray;
@@ -197,6 +198,9 @@
@GuardedBy("mLock")
private boolean mSystemReady;
+ @GuardedBy("mLock")
+ private PermissionPolicyInternal mPermissionPolicyInternal;
+
/**
* For each foreground/background permission the mapping:
* Background permission -> foreground permissions
@@ -1080,6 +1084,13 @@
boolean softRestricted = bp.isSoftRestricted();
for (int userId : currentUserIds) {
+ // If permission policy is not ready we don't deal with restricted
+ // permissions as the policy may whitelist some permissions. Once
+ // the policy is initialized we would re-evaluate permissions.
+ final boolean permissionPolicyInitialized =
+ mPermissionPolicyInternal != null
+ && mPermissionPolicyInternal.isInitialized(userId);
+
PermissionState permState = origPermissions
.getRuntimePermissionState(perm, userId);
int flags = permState != null ? permState.getFlags() : 0;
@@ -1094,7 +1105,7 @@
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
- if (hardRestricted) {
+ if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
@@ -1107,7 +1118,7 @@
}
}
// If soft restricted we allow holding in a restricted form
- } else if (softRestricted) {
+ } else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
@@ -1126,7 +1137,8 @@
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
wasChanged = true;
// Hard restricted permissions cannot be held.
- } else if (!hardRestricted || restrictionExempt) {
+ } else if (!permissionPolicyInitialized
+ || (!hardRestricted || restrictionExempt)) {
if (permState != null && permState.isGranted()) {
if (permissionsState.grantRuntimePermission(bp, userId)
== PERMISSION_OPERATION_FAILURE) {
@@ -1155,33 +1167,28 @@
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
- if ((hardRestricted || softRestricted)
- && !restrictionExempt && !restrictionApplied) {
+ if (permissionPolicyInitialized
+ && (hardRestricted || softRestricted)
+ && !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
- if (!(hardRestricted || softRestricted) || restrictionExempt) {
- if (restrictionApplied) {
- flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
- // Dropping restriction on a legacy app requires a review.
- if (!appSupportsRuntimePermissions) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ if (permissionPolicyInitialized) {
+ if (!(hardRestricted || softRestricted) || restrictionExempt) {
+ if (restrictionApplied) {
+ flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
+ // Dropping restriction on a legacy app implies a review
+ if (!appSupportsRuntimePermissions) {
+ flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+ wasChanged = true;
}
- wasChanged = true;
}
}
- if (hardRestricted && !restrictionExempt
- && (flags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
- // Applying a hard restriction implies revoking it. This might
- // lead to a system-fixed, revoked permission.
- flags &= ~FLAG_PERMISSION_SYSTEM_FIXED;
- wasChanged = true;
- }
-
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
@@ -1216,6 +1223,13 @@
boolean softRestricted = bp.isSoftRestricted();
for (int userId : currentUserIds) {
+ // If permission policy is not ready we don't deal with restricted
+ // permissions as the policy may whitelist some permissions. Once
+ // the policy is initialized we would re-evaluate permissions.
+ final boolean permissionPolicyInitialized =
+ mPermissionPolicyInternal != null
+ && mPermissionPolicyInternal.isInitialized(userId);
+
boolean wasChanged = false;
boolean restrictionExempt =
@@ -1226,7 +1240,7 @@
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
- if (hardRestricted) {
+ if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
@@ -1239,7 +1253,7 @@
}
}
// If soft restricted we allow holding in a restricted form
- } else if (softRestricted) {
+ } else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
@@ -1258,7 +1272,8 @@
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
wasChanged = true;
// Hard restricted permissions cannot be held.
- } else if (!hardRestricted || restrictionExempt) {
+ } else if (!permissionPolicyInitialized ||
+ (!hardRestricted || restrictionExempt)) {
if (permissionsState.grantRuntimePermission(bp, userId) !=
PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
@@ -1274,22 +1289,25 @@
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
- if ((hardRestricted || softRestricted)
- && !restrictionExempt && !restrictionApplied) {
+ if (permissionPolicyInitialized
+ && (hardRestricted || softRestricted)
+ && !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
- if (!(hardRestricted || softRestricted) || restrictionExempt) {
- if (restrictionApplied) {
- flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
- // Dropping restriction on a legacy app requires a review.
- if (!appSupportsRuntimePermissions) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ if (permissionPolicyInitialized) {
+ if (!(hardRestricted || softRestricted) || restrictionExempt) {
+ if (restrictionApplied) {
+ flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
+ // Dropping restriction on a legacy app implies a review
+ if (!appSupportsRuntimePermissions) {
+ flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+ wasChanged = true;
}
- wasChanged = true;
}
}
@@ -2900,6 +2918,7 @@
}
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
+ mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
}
private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 7760c1e..6084c67 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Intent;
/**
@@ -26,6 +27,19 @@
public abstract class PermissionPolicyInternal {
/**
+ * Callback for initializing the permission policy service.
+ */
+ public interface OnInitializedCallback {
+
+ /**
+ * Called when initialized for the given user.
+ *
+ * @param userId The initialized user.
+ */
+ void onInitialized(@UserIdInt int userId);
+ }
+
+ /**
* Check whether an activity should be started.
*
* @param intent the {@link Intent} for the activity start
@@ -36,4 +50,17 @@
*/
public abstract boolean checkStartActivity(@NonNull Intent intent, int callingUid,
@Nullable String callingPackage);
+
+ /**
+ * @return Whether the policy is initialized for a user.
+ */
+ public abstract boolean isInitialized(@UserIdInt int userId);
+
+ /**
+ * Set a callback for users being initialized. If the user is already
+ * initialized the callback will not be invoked.
+ *
+ * @param callback The callback to register.
+ */
+ public abstract void setOnInitializedCallback(@NonNull OnInitializedCallback callback);
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 037293f..3d543c9 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -66,6 +66,7 @@
import com.android.server.SystemService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -86,6 +87,10 @@
@GuardedBy("mLock")
private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
+ /** Callbacks for when a user is initialized */
+ @GuardedBy("mLock")
+ private OnInitializedCallback mOnInitializedCallback;
+
/**
* Whether an async {@link #synchronizePackagePermissionsAndAppOpsForUser} is currently
* scheduled for a package/user.
@@ -240,12 +245,20 @@
grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
+ final OnInitializedCallback callback;
+
synchronized (mLock) {
mIsStarted.put(userId, true);
+ callback = mOnInitializedCallback;
}
// Force synchronization as permissions might have changed
synchronizePermissionsAndAppOpsForUser(userId);
+
+ // Tell observers we are initialized for this user.
+ if (callback != null) {
+ callback.onInitialized(userId);
+ }
}
@Override
@@ -798,7 +811,7 @@
@Override
public boolean checkStartActivity(@NonNull Intent intent, int callingUid,
@Nullable String callingPackage) {
- if (callingPackage != null && isActionRemovedForCallingPackage(intent.getAction(),
+ if (callingPackage != null && isActionRemovedForCallingPackage(intent, callingUid,
callingPackage)) {
Slog.w(LOG_TAG, "Action Removed: starting " + intent.toString() + " from "
+ callingPackage + " (uid=" + callingUid + ")");
@@ -807,12 +820,25 @@
return true;
}
+ @Override
+ public boolean isInitialized(int userId) {
+ return isStarted(userId);
+ }
+
+ @Override
+ public void setOnInitializedCallback(@NonNull OnInitializedCallback callback) {
+ synchronized (mLock) {
+ mOnInitializedCallback = callback;
+ }
+ }
+
/**
* Check if the intent action is removed for the calling package (often based on target SDK
* version). If the action is removed, we'll silently cancel the activity launch.
*/
- private boolean isActionRemovedForCallingPackage(@Nullable String action,
+ private boolean isActionRemovedForCallingPackage(@NonNull Intent intent, int callingUid,
@NonNull String callingPackage) {
+ String action = intent.getAction();
if (action == null) {
return false;
}
@@ -821,15 +847,19 @@
case Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT: {
ApplicationInfo applicationInfo;
try {
- applicationInfo = getContext().getPackageManager().getApplicationInfo(
- callingPackage, 0);
+ applicationInfo = getContext().getPackageManager().getApplicationInfoAsUser(
+ callingPackage, 0, UserHandle.getUserId(callingUid));
+ if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+ // Applications targeting Q or higher should use
+ // RoleManager.createRequestRoleIntent() instead.
+ return true;
+ }
} catch (PackageManager.NameNotFoundException e) {
Slog.i(LOG_TAG, "Cannot find application info for " + callingPackage);
- return false;
}
- // Applications targeting Q should use RoleManager.createRequestRoleIntent()
- // instead.
- return applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+ // Make sure RequestRoleActivity can know the calling package if we allow it.
+ intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+ return false;
}
default:
return false;
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index d53f685..c1a6dbd 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -28,11 +28,14 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+import static java.lang.Integer.min;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.UserHandle;
@@ -73,6 +76,41 @@
};
/**
+ * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
+ * what to set, always compute the combined targetSDK.
+ *
+ * @param context A context
+ * @param appInfo The app that is changed
+ * @param user The user the app belongs to
+ *
+ * @return The minimum targetSDK of all apps sharing the uid of the app
+ */
+ private static int getMinimumTargetSDK(@NonNull Context context,
+ @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) {
+ PackageManager pm = context.getPackageManager();
+
+ int minimumTargetSDK = appInfo.targetSdkVersion;
+
+ String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
+ if (uidPkgs != null) {
+ for (String uidPkg : uidPkgs) {
+ if (!uidPkg.equals(appInfo.packageName)) {
+ ApplicationInfo uidPkgInfo;
+ try {
+ uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue;
+ }
+
+ minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion);
+ }
+ }
+ }
+
+ return minimumTargetSDK;
+ }
+
+ /**
* Get the policy for a soft restricted permission.
*
* @param context A context to use
@@ -99,12 +137,36 @@
final int targetSDK;
if (appInfo != null) {
- flags = context.getPackageManager().getPermissionFlags(permission,
- appInfo.packageName, user);
+ PackageManager pm = context.getPackageManager();
+ flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- hasRequestedLegacyExternalStorage = appInfo.hasRequestedLegacyExternalStorage();
- targetSDK = appInfo.targetSdkVersion;
+ targetSDK = getMinimumTargetSDK(context, appInfo, user);
+
+ boolean hasAnyRequestedLegacyExternalStorage =
+ appInfo.hasRequestedLegacyExternalStorage();
+
+ // hasRequestedLegacyExternalStorage is per package. To make sure two apps in
+ // the same shared UID do not fight over what to set, always compute the
+ // combined hasRequestedLegacyExternalStorage
+ String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
+ if (uidPkgs != null) {
+ for (String uidPkg : uidPkgs) {
+ if (!uidPkg.equals(appInfo.packageName)) {
+ ApplicationInfo uidPkgInfo;
+ try {
+ uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue;
+ }
+
+ hasAnyRequestedLegacyExternalStorage |=
+ uidPkgInfo.hasRequestedLegacyExternalStorage();
+ }
+ }
+ }
+
+ hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;
} else {
flags = 0;
applyRestriction = false;
@@ -155,7 +217,7 @@
final int flags = context.getPackageManager().getPermissionFlags(permission,
appInfo.packageName, user);
isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- targetSDK = appInfo.targetSdkVersion;
+ targetSDK = getMinimumTargetSDK(context, appInfo, user);
} else {
isWhiteListed = false;
targetSDK = 0;
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index c7e241b..17392e0 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -33,6 +33,9 @@
"options": [
{
"include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ },
+ {
+ "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest"
}
]
},
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e107c9a..08c1bb5 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -86,9 +86,9 @@
private static final String TAG = "RollbackManager";
- // Rollbacks expire after 48 hours.
+ // Rollbacks expire after 14 days.
private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
- TimeUnit.HOURS.toMillis(48);
+ TimeUnit.DAYS.toMillis(14);
// Lock used to synchronize accesses to in-memory rollback data
// structures. By convention, methods with the suffix "Locked" require
@@ -1289,7 +1289,8 @@
private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
- return a.getPackageName().equals(b.getPackageName())
+ return a != null && b != null
+ && a.getPackageName().equals(b.getPackageName())
&& a.getLongVersionCode() == b.getLongVersionCode();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9908b36..b0f1e5d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1172,8 +1172,16 @@
return false;
}
final int displayId = display.getDisplayId();
- return displayId == DEFAULT_DISPLAY
- || mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+ if (displayId == DEFAULT_DISPLAY) {
+ return true;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
void forEachDisplayConnector(Consumer<DisplayConnector> action) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 03cae42..eab5e0d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -79,6 +79,7 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
@@ -540,6 +541,18 @@
// If the app was already visible, don't reset the waitingToShow state.
if (isHidden()) {
waitingToShow = true;
+
+ // Let's reset the draw state in order to prevent the starting window to be
+ // immediately dismissed when the app still has the surface.
+ forAllWindows(w -> {
+ if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
+ w.mWinAnimator.resetDrawState();
+
+ // Force add to mResizingWindows, so that we are guaranteed to get
+ // another reportDrawn callback.
+ w.resetLastContentInsets();
+ }
+ }, true /* traverseTopToBottom */);
}
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index b90d602..c0942c9 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -936,6 +936,7 @@
}
}
+ t.setEarlyWakeup();
setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 14585c5..19fbfed 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -292,6 +292,10 @@
private int getPreferredLaunchDisplay(@Nullable TaskRecord task,
@Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) {
+ if (!mSupervisor.mService.mSupportsMultiDisplay) {
+ return DEFAULT_DISPLAY;
+ }
+
int displayId = INVALID_DISPLAY;
final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
if (optionLaunchId != INVALID_DISPLAY) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fb57d73..436a5c7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7670,10 +7670,12 @@
isDown = motionEvent.getAction() == MotionEvent.ACTION_DOWN;
isUp = motionEvent.getAction() == MotionEvent.ACTION_UP;
}
+ final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
// For ACTION_DOWN, syncInputTransactions before injecting input.
+ // For all mouse events, also sync before injecting.
// For ACTION_UP, sync after injecting.
- if (isDown) {
+ if (isDown || isMouseEvent) {
syncInputTransactions();
}
final boolean result =
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 43ad091..703fe4a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1623,7 +1623,7 @@
|| !mRelayoutCalled
|| (atoken == null && mToken.isHidden())
|| (atoken != null && atoken.hiddenRequested)
- || isParentWindowHidden()
+ || isParentWindowGoneForLayout()
|| (mAnimatingExit && !isAnimatingLw())
|| mDestroying;
}
@@ -3795,6 +3795,11 @@
return parent != null && parent.mHidden;
}
+ private boolean isParentWindowGoneForLayout() {
+ final WindowState parent = getParentWindow();
+ return parent != null && parent.isGoneForLayoutLw();
+ }
+
void setWillReplaceWindow(boolean animate) {
for (int i = mChildren.size() - 1; i >= 0; i--) {
final WindowState c = mChildren.get(i);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6f7af3f..656af98 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1974,6 +1974,11 @@
}
traceEnd();
+ // Permission policy service
+ traceBeginAndSlog("StartPermissionPolicyService");
+ mSystemServiceManager.startService(PermissionPolicyService.class);
+ traceEnd();
+
traceBeginAndSlog("MakePackageManagerServiceReady");
mPackageManagerService.systemReady();
traceEnd();
@@ -2008,11 +2013,6 @@
mSystemServiceManager.startBootPhase(SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
traceEnd();
- // Permission policy service
- traceBeginAndSlog("StartPermissionPolicyService");
- mSystemServiceManager.startService(PermissionPolicyService.class);
- traceEnd();
-
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index f918149..58614c6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1225,6 +1225,22 @@
assertEquals(startingBounds, adjustedBounds);
}
+ @Test
+ public void testNoMultiDisplaySupports() {
+ final boolean orgValue = mService.mSupportsMultiDisplay;
+ final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN);
+ mCurrent.mPreferredDisplayId = display.mDisplayId;
+
+ try {
+ mService.mSupportsMultiDisplay = false;
+ assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+ mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+ assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
+ } finally {
+ mService.mSupportsMultiDisplay = orgValue;
+ }
+ }
+
private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
display.setWindowingMode(windowingMode);
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index a1bea4d..d4ed923 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.RemoteException;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -28,7 +29,9 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import java.util.ArrayList;
@@ -140,9 +143,12 @@
try {
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
- boolean hasPrivileges = telephonyManager != null &&
- telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
+ R.array.config_restrictedPreinstalledCarrierApps);
+ boolean hasPrivileges = telephonyManager != null
+ && telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+ && !ArrayUtils.contains(restrictedCarrierApps, packageName);
// add hiddenUntilInstalled flag for carrier apps and associated apps
packageManager.setSystemAppHiddenUntilInstalled(packageName, true);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index cd659e2..d37c4a2 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -190,7 +190,7 @@
* has changed. One extra {@link #EXTRA_WIFI_P2P_INFO} provides the p2p connection info in
* the form of a {@link WifiP2pInfo} object. Another extra {@link #EXTRA_NETWORK_INFO} provides
* the network info in the form of a {@link android.net.NetworkInfo}. A third extra provides
- * the details of the group.
+ * the details of the group and may contain a {@code null}.
*
* All of these permissions are required to receive this broadcast:
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and