[automerger skipped] resolve merge conflicts of 9acd72c60b0965fdb1da1bce39b12e0d9c242a06 to stage-aosp-master
am: 3020e050d0 -s ours
am skip reason: change_id I2fcf25264c62acc801f9e62967072cd04e4641e7 with SHA1 be2b0a71d7 is in history
Change-Id: I58805df7054e714688c744883cb722bc8e5a7209
diff --git a/api/test-current.txt b/api/test-current.txt
index d2dbacb..acc1b4a 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);
@@ -2313,6 +2314,7 @@
}
public static interface DeviceConfig.WindowManager {
+ field public static final String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE = "system_gestures_excluded_by_pre_q_sticky_immersive";
field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
}
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 0f40d59..9173e21 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1004,8 +1004,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.
@@ -1051,6 +1050,8 @@
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
+ data.autofillOptions = autofillOptions;
+ data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
sendMessage(H.BIND_APPLICATION, data);
}
@@ -6132,7 +6133,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 60f1424..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
@@ -2507,7 +2514,7 @@
}
/**
- * @return The duration of the operation in milliseconds.
+ * @return The duration of the operation in milliseconds. The duration is in wall time.
*/
public long getDuration() {
return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
@@ -2515,7 +2522,7 @@
/**
* Return the duration in milliseconds the app accessed this op while
- * in the foreground.
+ * in the foreground. The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -2534,7 +2541,7 @@
/**
* Return the duration in milliseconds the app accessed this op while
- * in the background.
+ * in the background. The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -2553,7 +2560,7 @@
/**
* Return the duration in milliseconds the app accessed this op for
- * a given range of UID states.
+ * a given range of UID states. The duration is in wall time.
*
* @param fromUidState The UID state for which to query. Could be one of
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
@@ -3968,6 +3975,7 @@
/**
* Gets the total duration the app op was accessed (performed) in the foreground.
+ * The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -3986,6 +3994,7 @@
/**
* Gets the total duration the app op was accessed (performed) in the background.
+ * The duration is in wall time.
*
* @param flags The flags which are any combination of
* {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
@@ -4004,7 +4013,7 @@
/**
* Gets the total duration the app op was accessed (performed) for a given
- * range of UID states.
+ * range of UID states. The duration is in wall time.
*
* @param fromUidState The UID state from which to query. Could be one of
* {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 2d18838..bb775fc 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import android.annotation.UnsupportedAppUsage;
import android.app.ClientTransactionHandler;
@@ -38,10 +39,11 @@
@UnsupportedAppUsage
private List<ReferrerIntent> mIntents;
+ private boolean mResume;
@Override
public int getPostExecutionState() {
- return ON_RESUME;
+ return mResume ? ON_RESUME : UNDEFINED;
}
@Override
@@ -58,12 +60,13 @@
private NewIntentItem() {}
/** Obtain an instance initialized with provided params. */
- public static NewIntentItem obtain(List<ReferrerIntent> intents) {
+ public static NewIntentItem obtain(List<ReferrerIntent> intents, boolean resume) {
NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class);
if (instance == null) {
instance = new NewIntentItem();
}
instance.mIntents = intents;
+ instance.mResume = resume;
return instance;
}
@@ -71,6 +74,7 @@
@Override
public void recycle() {
mIntents = null;
+ mResume = false;
ObjectPool.recycle(this);
}
@@ -80,11 +84,13 @@
/** Write to Parcel. */
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mResume);
dest.writeTypedList(mIntents, flags);
}
/** Read from Parcel. */
private NewIntentItem(Parcel in) {
+ mResume = in.readBoolean();
mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
}
@@ -108,18 +114,19 @@
return false;
}
final NewIntentItem other = (NewIntentItem) o;
- return Objects.equals(mIntents, other.mIntents);
+ return mResume == other.mResume && Objects.equals(mIntents, other.mIntents);
}
@Override
public int hashCode() {
int result = 17;
+ result = 31 * result + (mResume ? 1 : 0);
result = 31 * result + mIntents.hashCode();
return result;
}
@Override
public String toString() {
- return "NewIntentItem{intents=" + mIntents + "}";
+ return "NewIntentItem{intents=" + mIntents + ",resume=" + mResume + "}";
}
}
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b0b1874..23fbefb 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -17,7 +17,6 @@
package android.content.pm;
import android.Manifest;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -177,8 +176,7 @@
mContext.registerReceiver(mUserRemovedReceiver, userFilter);
}
- @VisibleForTesting
- protected void handlePackageEvent(Intent intent, int userId) {
+ private void handlePackageEvent(Intent intent, int userId) {
// Don't regenerate the services map when the package is removed or its
// ASEC container unmounted as a step in replacement. The subsequent
// _ADDED / _AVAILABLE call will regenerate the map in the final state.
@@ -240,9 +238,6 @@
public void invalidateCache(int userId) {
synchronized (mServicesLock) {
- if (DEBUG) {
- Slog.d(TAG, "invalidating cache for " + userId + " " + mInterfaceName);
- }
final UserServices<V> user = findOrCreateUserLocked(userId);
user.services = null;
onServicesChangedLocked(userId);
@@ -472,48 +467,34 @@
* or null to assume that everything is affected.
* @param userId the user for whom to update the services map.
*/
- private void generateServicesMap(@Nullable int[] changedUids, int userId) {
+ private void generateServicesMap(int[] changedUids, int userId) {
if (DEBUG) {
Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
+ Arrays.toString(changedUids));
}
+ final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
+ final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ try {
+ ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+ if (info == null) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
+ continue;
+ }
+ serviceInfos.add(info);
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
+ }
+ }
+
synchronized (mServicesLock) {
final UserServices<V> user = findOrCreateUserLocked(userId);
- final boolean cacheInvalid = user.services == null;
- if (cacheInvalid) {
+ final boolean firstScan = user.services == null;
+ if (firstScan) {
user.services = Maps.newHashMap();
}
- final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
- final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
-
- for (ResolveInfo resolveInfo : resolveInfos) {
- try {
- // when changedUids == null, we want to do a rescan of everything, this means
- // it's the initial scan, and containsUid will trivially return true
- // when changedUids != null, we got here because a package changed, but
- // invalidateCache could have been called (thus user.services == null), and we
- // should query from PackageManager again
- if (!cacheInvalid
- && !containsUid(
- changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
- if (DEBUG) {
- Slog.d(TAG, "Skipping parseServiceInfo for " + resolveInfo);
- }
- continue;
- }
- ServiceInfo<V> info = parseServiceInfo(resolveInfo);
- if (info == null) {
- Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
- continue;
- }
- serviceInfos.add(info);
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
- }
- }
-
StringBuilder changes = new StringBuilder();
boolean changed = false;
for (ServiceInfo<V> info : serviceInfos) {
@@ -534,7 +515,7 @@
changed = true;
user.services.put(info.type, info);
user.persistentServices.put(info.type, info.uid);
- if (!(user.mPersistentServicesFileDidNotExist && cacheInvalid)) {
+ if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
notifyListener(info.type, userId, false /* removed */);
}
} else if (previousUid == info.uid) {
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/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 920eb4b..e30ba38 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -325,6 +325,17 @@
*/
@TestApi
String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+
+ /**
+ * Key for controlling whether system gestures are implicitly excluded by windows requesting
+ * sticky immersive mode from apps that are targeting an SDK prior to Q.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
+ * @hide
+ */
+ @TestApi
+ String KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE =
+ "system_gestures_excluded_by_pre_q_sticky_immersive";
}
private static final Object sLock = new Object();
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/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/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f9d27bb..00206fc 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1534,7 +1534,11 @@
if (driList.get(i).getResolvedComponentName().equals(
resultList.get(j).getTargetComponent())) {
ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
- ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo);
+ // Incoming results are ordered but without a score. Create a score
+ // based on the index in order to be sorted appropriately when joined
+ // with legacy direct share api results.
+ float score = Math.max(1.0f - (0.05f * j), 0.0f);
+ ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score);
chooserTargets.add(chooserTarget);
if (mDirectShareAppTargetCache != null && appTargets != null) {
mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
@@ -1580,7 +1584,8 @@
return false;
}
- private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) {
+ private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut,
+ float score) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
Bundle extras = new Bundle();
extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
@@ -1591,7 +1596,7 @@
null,
// The ranking score for this target (0.0-1.0); the system will omit items with low
// scores when there are too many Direct Share items.
- 1.0f,
+ score,
// The name of the component to be launched if this target is chosen.
shareShortcut.getTargetComponent().clone(),
// The extra values here will be merged into the Intent when this target is chosen.
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/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f905ea2..58ce03b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -889,7 +889,8 @@
: mAdapterView.getCheckedItemPosition();
boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
- if (!ri.handleAllWebDataURI && id == R.id.button_always) {
+ if (mUseLayoutForBrowsables
+ && !ri.handleAllWebDataURI && id == R.id.button_always) {
showSettingsForSelected(ri);
} else {
startSelected(which, id == R.id.button_always, hasIndexBeenFiltered);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6f4f337..fe66cf9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1311,7 +1311,7 @@
return semiTransparentBarColor;
} else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
return Color.BLACK;
- } else if (scrimTransparent && barColor == Color.TRANSPARENT) {
+ } else if (scrimTransparent && Color.alpha(barColor) == 0) {
boolean light = (sysuiVis & lightSysuiFlag) != 0;
return light ? SCRIM_LIGHT : semiTransparentBarColor;
} else {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fef4dcd..a3fccd9 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 did not whitelist 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 did not whitelist 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 did not whitelist 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 did not whitelist 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 did not whitelist 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 did not whitelist 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,30 +830,35 @@
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 did not whitelist 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)}.
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
@@ -916,6 +946,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 did not whitelist 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 +992,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 did not whitelist 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 +1016,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 did not whitelist 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 +1033,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 did not whitelist 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.
-->
@@ -4616,8 +4662,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 +4986,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/symbols.xml b/core/res/res/values/symbols.xml
index 544fd44..0367c27 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3818,4 +3818,8 @@
<java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
<java-symbol type="bool" name="config_inflateSignalStrength" />
+
+ <java-symbol type="drawable" name="android_logotype" />
+ <java-symbol type="layout" name="platlogo_layout" />
+
</resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 711eaa7..c50cbe3 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -19,6 +19,8 @@
import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
@@ -31,6 +33,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
import android.content.Intent;
@@ -45,9 +48,13 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.content.ReferrerIntent;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -307,6 +314,24 @@
assertEquals(400, activity.mConfig.smallestScreenWidthDp);
}
+ @Test
+ public void testResumeAfterNewIntent() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final ArrayList<ReferrerIntent> rIntents = new ArrayList<>();
+ rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity"));
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
+ });
+ assertThat(activity.isResumed()).isFalse();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true));
+ });
+ assertThat(activity.isResumed()).isTrue();
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
* to try to push activity configuration to the activity for the given sequence number.
@@ -386,6 +411,16 @@
return transaction;
}
+ private static ClientTransaction newNewIntentTransaction(Activity activity,
+ List<ReferrerIntent> intents, boolean resume) {
+ final NewIntentItem item = NewIntentItem.obtain(intents, resume);
+
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addCallback(item);
+
+ return transaction;
+ }
+
private static ClientTransaction newTransaction(Activity activity) {
final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
return ClientTransaction.obtain(appThread, activity.getActivityToken());
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 1e49c0a..37d21f0 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -214,15 +214,15 @@
@Test
public void testRecycleNewIntentItem() {
- NewIntentItem emptyItem = NewIntentItem.obtain(null);
- NewIntentItem item = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem emptyItem = NewIntentItem.obtain(null, false);
+ NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false);
assertNotSame(item, emptyItem);
assertFalse(item.equals(emptyItem));
item.recycle();
assertEquals(item, emptyItem);
- NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem item2 = NewIntentItem.obtain(referrerIntentList(), false);
assertSame(item, item2);
assertFalse(item2.equals(emptyItem));
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 8fe1279..51da0c8 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -128,7 +128,7 @@
@Test
public void testNewIntent() {
// Write to parcel
- NewIntentItem item = NewIntentItem.obtain(referrerIntentList());
+ NewIntentItem item = NewIntentItem.obtain(referrerIntentList(), false);
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index c8150b1..365e97d 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.content.Intent;
import android.content.res.Resources;
import android.os.FileUtils;
import android.os.Parcel;
@@ -190,36 +189,6 @@
assertEquals(0, cache.getPersistentServicesSize(u1));
}
- /**
- * Check that an optimization to skip a call to PackageManager handles an invalidated cache.
- *
- * We added an optimization in generateServicesMap to only query PackageManager for packages
- * that have been changed, because if a package is unchanged, we have already cached the
- * services info for it, so we can save a query to PackageManager (and save some memory).
- * However, if invalidateCache was called, we cannot optimize, and must do a full query.
- * The initial optimization was buggy because it failed to check for an invalidated cache, and
- * only scanned the changed packages, given in the ACTION_PACKAGE_CHANGED intent (b/122912184).
- */
- public void testParseServiceInfoOptimizationHandlesInvalidatedCache() {
- TestServicesCache cache = new TestServicesCache();
- cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
- cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
- assertEquals(2, cache.getAllServicesSize(U0));
-
- // simulate the client of the cache invalidating it
- cache.invalidateCache(U0);
-
- // there should be 0 services (userServices.services == null ) at this point, but we don't
- // call getAllServicesSize since that would force a full scan of packages,
- // instead we trigger a package change in a package that is in the list of services
- Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
- intent.putExtra(Intent.EXTRA_UID, UID1);
- cache.handlePackageEvent(intent, U0);
-
- // check that the optimization does a full query and caches both services
- assertEquals(2, cache.getAllServicesSize(U0));
- }
-
private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
TestServiceType type, int uid) {
final ComponentInfo info = new ComponentInfo();
@@ -297,11 +266,6 @@
map = new HashMap<>();
mServices.put(userId, map);
}
- // in actual cases, resolveInfo should always have a serviceInfo, since we specifically
- // query for intent services
- resolveInfo.serviceInfo = new android.content.pm.ServiceInfo();
- resolveInfo.serviceInfo.applicationInfo =
- new ApplicationInfo(serviceInfo.componentInfo.applicationInfo);
map.put(resolveInfo, serviceInfo);
}
@@ -340,11 +304,6 @@
public void onUserRemoved(int userId) {
super.onUserRemoved(userId);
}
-
- @Override
- public void handlePackageEvent(Intent intent, int userId) {
- super.handlePackageEvent(intent, userId);
- }
}
static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
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/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index df0dd7c..f1ab149 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -64,6 +64,9 @@
return nullptr;
}
+ auto gid = getgid();
+ auto uid = getuid();
+
char const** argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1));
for (size_t i = 0; i < argv.size(); i++) {
argv0[i] = argv[i].c_str();
@@ -75,6 +78,16 @@
PLOG(ERROR) << "fork";
return nullptr;
case 0: // child
+ if (setgid(gid) != 0) {
+ PLOG(ERROR) << "setgid";
+ exit(1);
+ }
+
+ if (setuid(uid) != 0) {
+ PLOG(ERROR) << "setuid";
+ exit(1);
+ }
+
close(stdout[0]);
if (dup2(stdout[1], STDOUT_FILENO) == -1) {
abort();
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 63a4510..c3dd3fe 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -46,14 +46,14 @@
import java.util.concurrent.Executor;
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ *
* Allows an app to interact with an active {@link MediaSession2} or a
* {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
* commands can be sent to the session.
- * <p>
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
*/
public class MediaController2 implements AutoCloseable {
static final String TAG = "MediaController2";
@@ -405,6 +405,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builder for {@link MediaController2}.
* <p>
* Any incoming event from the {@link MediaSession2} will be handled on the callback
@@ -502,9 +507,12 @@
}
/**
- * Interface for listening to change in activeness of the {@link MediaSession2}.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Interface for listening to change in activeness of the {@link MediaSession2}.
*/
public abstract static class ControllerCallback {
/**
diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java
index b3edf3f..081e76a 100644
--- a/media/apex/java/android/media/MediaSession2.java
+++ b/media/apex/java/android/media/MediaSession2.java
@@ -52,13 +52,13 @@
import java.util.concurrent.Executor;
/**
- * Allows a media app to expose its transport controls and playback information in a process to
- * other processes including the Android framework and other apps.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps.
*/
public class MediaSession2 implements AutoCloseable {
static final String TAG = "MediaSession2";
@@ -481,6 +481,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builder for {@link MediaSession2}.
* <p>
* Any incoming event from the {@link MediaController2} will be handled on the callback
@@ -616,9 +621,12 @@
}
/**
- * Information of a controller.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Information of a controller.
*/
public static final class ControllerInfo {
private final RemoteUserInfo mRemoteUserInfo;
@@ -807,9 +815,12 @@
}
/**
- * Callback to be called for all incoming commands from {@link MediaController2}s.
- * <p>
* This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Callback to be called for all incoming commands from {@link MediaController2}s.
*/
public abstract static class SessionCallback {
/**
diff --git a/media/apex/java/android/media/MediaSession2Service.java b/media/apex/java/android/media/MediaSession2Service.java
index ee584e5..f6fd509 100644
--- a/media/apex/java/android/media/MediaSession2Service.java
+++ b/media/apex/java/android/media/MediaSession2Service.java
@@ -44,12 +44,12 @@
import java.util.Map;
/**
- * Service containing {@link MediaSession2}.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Service containing {@link MediaSession2}.
*/
public abstract class MediaSession2Service extends Service {
/**
@@ -287,6 +287,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
* foreground service to keep playback running in the background. It's highly recommended to
* show media style notification here.
diff --git a/media/apex/java/android/media/Session2Command.java b/media/apex/java/android/media/Session2Command.java
index 7c752e1..26f4568 100644
--- a/media/apex/java/android/media/Session2Command.java
+++ b/media/apex/java/android/media/Session2Command.java
@@ -26,6 +26,11 @@
import java.util.Objects;
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
* <p>
* If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
@@ -35,11 +40,6 @@
* Refer to the
* <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
* class for the list of valid commands.
- * <p>
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
*/
public final class Session2Command implements Parcelable {
/**
@@ -162,6 +162,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Contains the result of {@link Session2Command}.
*/
public static final class Result {
diff --git a/media/apex/java/android/media/Session2CommandGroup.java b/media/apex/java/android/media/Session2CommandGroup.java
index 06ae873..0ee5f62 100644
--- a/media/apex/java/android/media/Session2CommandGroup.java
+++ b/media/apex/java/android/media/Session2CommandGroup.java
@@ -28,13 +28,12 @@
import java.util.Set;
/**
- * A set of {@link Session2Command} which represents a command group.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
- * </p>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * A set of {@link Session2Command} which represents a command group.
*/
public final class Session2CommandGroup implements Parcelable {
private static final String TAG = "Session2CommandGroup";
@@ -131,6 +130,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Builds a {@link Session2CommandGroup} object.
*/
public static final class Builder {
diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java
index 6d499fa..6eb76b1 100644
--- a/media/apex/java/android/media/Session2Token.java
+++ b/media/apex/java/android/media/Session2Token.java
@@ -36,13 +36,13 @@
import java.util.Objects;
/**
- * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
- * If it's representing a session service, it may not be ongoing.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
+ * If it's representing a session service, it may not be ongoing.
* <p>
* This may be passed to apps by the session owner to allow them to create a
* {@link MediaController2} to communicate with the session.
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 569d11e..dec0140 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -119,6 +119,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
* created.
* <p>
@@ -192,6 +197,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the
* current user.
* <p>
@@ -335,12 +345,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param listener The listener to add
*/
@@ -350,12 +360,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param listener The listener to add
* @param handler The handler to call listener on.
@@ -366,12 +376,12 @@
}
/**
- * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
*
* @param userId The userId to listen for changes on
* @param listener The listener to add
@@ -402,6 +412,11 @@
}
/**
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
* Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
*
* @param listener The listener to remove.
@@ -765,13 +780,13 @@
}
/**
- * Listens for changes to the {@link #getSession2Tokens()}. This can be added
- * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
- * <p>
* This API is not generally intended for third party application developers.
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
+ * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session
+ * Library</a> for consistent behavior across all devices.
+ * <p>
+ * Listens for changes to the {@link #getSession2Tokens()}. This can be added
+ * using {@link #addOnSession2TokensChangedListener(OnSession2TokensChangedListener, Handler)}.
*/
public interface OnSession2TokensChangedListener {
/**
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/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 19e7b73..6e6c009 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -112,6 +112,9 @@
wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast
</string>
+ <!-- The minimum number of tiles to display in QuickSettings -->
+ <integer name="quick_settings_min_num_tiles">6</integer>
+
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8199ea3..591af82 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2009,7 +2009,7 @@
<string name="drag_to_remove_tiles">Drag here to remove</string>
<!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] -->
- <string name="drag_to_remove_disabled">You need at least 6 tiles</string>
+ <string name="drag_to_remove_disabled">You need at least <xliff:g id="min_num_tiles" example="6">%1$d</xliff:g> tiles</string>
<!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
<string name="qs_edit">Edit</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 9a0e9fc..c9d4957 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 = 5;
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()
@@ -122,6 +127,27 @@
long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
mWorker.getThreadHandler().post(
() -> mRenderer.updateAmbientMode(inAmbientMode, duration));
+ if (inAmbientMode && duration == 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/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 3cc8ec9a..69f243c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -91,7 +91,7 @@
@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 (!brightlineEnabled) {
mInternalFalsingManager = new FalsingManagerImpl(context);
} else {
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..45e97b3 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -84,7 +84,17 @@
void updateAwake(boolean awake, long duration) {
mAwake = awake;
mAnimator.setDuration(duration);
- animate();
+ if (!mAwake && duration == 0) {
+ // We are transiting from home to aod,
+ // since main thread is waiting for rendering finished, we only need draw
+ // the last state directly, which is a black screen.
+ mReveal = MIN_REVEAL;
+ mRevealListener.onRevealStart();
+ mRevealListener.onRevealStateChanged();
+ mRevealListener.onRevealEnd();
+ } else {
+ animate();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 8ed5424..2542abd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -54,7 +55,6 @@
import java.util.List;
public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
- private static final int MIN_NUM_TILES = 6;
private static final long DRAG_LENGTH = 100;
private static final float DRAG_SCALE = 1.2f;
public static final long MOVE_DURATION = 150;
@@ -79,6 +79,7 @@
private final ItemTouchHelper mItemTouchHelper;
private final ItemDecoration mDecoration;
private final AccessibilityManager mAccessibilityManager;
+ private final int mMinNumTiles;
private int mEditIndex;
private int mTileDividerIndex;
private boolean mNeedsFocus;
@@ -97,6 +98,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);
+ mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
}
public void setHost(QSTileHost host) {
@@ -247,15 +249,17 @@
return;
}
if (holder.getItemViewType() == TYPE_EDIT) {
- final int titleResId;
+ final String titleText;
+ Resources res = mContext.getResources();
if (mCurrentDrag == null) {
- titleResId = R.string.drag_to_add_tiles;
+ titleText = res.getString(R.string.drag_to_add_tiles);
} else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) {
- titleResId = R.string.drag_to_remove_disabled;
+ titleText = res.getString(R.string.drag_to_remove_disabled, mMinNumTiles);
} else {
- titleResId = R.string.drag_to_remove_tiles;
+ titleText = res.getString(R.string.drag_to_remove_tiles);
}
- ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId);
+
+ ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText);
return;
}
if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -337,7 +341,7 @@
}
private boolean canRemoveTiles() {
- return mCurrentSpecs.size() > MIN_NUM_TILES;
+ return mCurrentSpecs.size() > mMinNumTiles;
}
private void selectPosition(int position, View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index b70b45b..4049201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
import android.content.Context;
import android.content.res.Resources;
+import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Log;
@@ -43,6 +46,7 @@
import java.util.Stack;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -58,6 +62,8 @@
public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
private static final String TAG = "NotificationViewHierarchyManager";
+ private final Handler mHandler;
+
//TODO: change this top <Entry, List<Entry>>?
private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
mTmpChildOrderMap = new HashMap<>();
@@ -86,9 +92,13 @@
// Used to help track down re-entrant calls to our update methods, which will cause bugs.
private boolean mPerformingUpdate;
+ // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down
+ // the problem.
+ private boolean mIsHandleDynamicPrivacyChangeScheduled;
@Inject
public NotificationViewHierarchyManager(Context context,
+ @Named(MAIN_HANDLER_NAME) Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -97,6 +107,7 @@
Lazy<ShadeController> shadeController,
BubbleData bubbleData,
DynamicPrivacyController privacyController) {
+ mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
mGroupManager = groupManager;
mVisualStabilityManager = visualStabilityManager;
@@ -435,19 +446,33 @@
@Override
public void onDynamicPrivacyChanged() {
+ if (mPerformingUpdate) {
+ Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
+ }
+ // This listener can be called from updateNotificationViews() via a convoluted listener
+ // chain, so we post here to prevent a re-entrant call. See b/136186188
+ // TODO: Refactor away the need for this
+ if (!mIsHandleDynamicPrivacyChangeScheduled) {
+ mIsHandleDynamicPrivacyChangeScheduled = true;
+ mHandler.post(this::onHandleDynamicPrivacyChanged);
+ }
+ }
+
+ private void onHandleDynamicPrivacyChanged() {
+ mIsHandleDynamicPrivacyChangeScheduled = false;
updateNotificationViews();
}
private void beginUpdate() {
if (mPerformingUpdate) {
- throw new IllegalStateException("Re-entrant code during update.");
+ Log.wtf(TAG, "Re-entrant code during update", new Exception());
}
mPerformingUpdate = true;
}
private void endUpdate() {
if (!mPerformingUpdate) {
- throw new IllegalStateException("Manager state has become desynced.");
+ Log.wtf(TAG, "Manager state has become desynced", new Exception());
}
mPerformingUpdate = false;
}
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..a423adf 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
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..7d6799b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1213,6 +1213,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));
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/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index c476d80..5103e8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -78,13 +82,19 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
+ private TestableLooper mTestableLooper;
+ private Handler mHandler;
private NotificationViewHierarchyManager mViewHierarchyManager;
private NotificationTestHelper mHelper;
+ private boolean mMadeReentrantCall = false;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
+ mTestableLooper = TestableLooper.get(this);
+ Assert.sMainLooper = mTestableLooper.getLooper();
+ mHandler = Handler.createAsync(mTestableLooper.getLooper());
+
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
@@ -97,7 +107,7 @@
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
- mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
() -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class));
Dependency.get(InitController.class).executePostInitTasks();
@@ -212,9 +222,60 @@
verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
}
+ @Test
+ public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() {
+ // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews()
+ mViewHierarchyManager.updateNotificationViews();
+
+ // THEN onNotificationViewUpdateFinished() is only called once
+ verify(mListContainer).onNotificationViewUpdateFinished();
+
+ // WHEN we drain the looper
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called a second time (for the reentrant call)
+ verify(mListContainer, times(2)).onNotificationViewUpdateFinished();
+ }
+
+ @Test
+ public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() {
+ // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews()
+ mMadeReentrantCall = false;
+ doAnswer((invocation) -> {
+ if (!mMadeReentrantCall) {
+ mMadeReentrantCall = true;
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ return null;
+ }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+
+ // WHEN we call updateNotificationViews() and drain the looper
+ mViewHierarchyManager.updateNotificationViews();
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ clearInvocations(mListContainer);
+ mTestableLooper.processAllMessages();
+
+ // THEN updateNotificationViews() is called only one more time
+ verify(mListContainer).onNotificationViewUpdateFinished();
+ }
+
private class FakeListContainer implements NotificationListContainer {
final LinearLayout mLayout = new LinearLayout(mContext);
final List<View> mRows = Lists.newArrayList();
+ private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
@Override
public void setChildTransferInProgress(boolean childTransferInProgress) {}
@@ -263,7 +324,11 @@
}
@Override
- public void setMaxDisplayedNotifications(int maxNotifications) {}
+ public void setMaxDisplayedNotifications(int maxNotifications) {
+ if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
+ mViewHierarchyManager.onDynamicPrivacyChanged();
+ }
+ }
@Override
public ViewGroup getViewParentForNotification(NotificationEntry entry) {
@@ -298,5 +363,7 @@
return false;
}
+ @Override
+ public void onNotificationViewUpdateFinished() { }
}
}
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/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index cba9674..6db9702 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -88,6 +88,7 @@
import com.android.internal.util.HexDump;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.server.compat.CompatConfig;
import java.io.BufferedReader;
import java.io.File;
@@ -288,6 +289,8 @@
return runNoHomeScreen(pw);
case "wait-for-broadcast-idle":
return runWaitForBroadcastIdle(pw);
+ case "compat":
+ return runCompat(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2865,6 +2868,50 @@
return 0;
}
+ private int runCompat(PrintWriter pw) {
+ final CompatConfig config = CompatConfig.get();
+ String toggleValue = getNextArgRequired();
+ long changeId;
+ String changeIdString = getNextArgRequired();
+ try {
+ changeId = Long.parseLong(changeIdString);
+ } catch (NumberFormatException e) {
+ changeId = config.lookupChangeId(changeIdString);
+ }
+ if (changeId == -1) {
+ pw.println("Unknown or invalid change: '" + changeIdString + "'.");
+ }
+ String packageName = getNextArgRequired();
+ switch(toggleValue) {
+ case "enable":
+ if (!config.addOverride(changeId, packageName, true)) {
+ pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
+ + " could have no effect.");
+ }
+ pw.println("Enabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "disable":
+ if (!config.addOverride(changeId, packageName, false)) {
+ pw.println("Warning! Change " + changeId + " is not known yet. Disabling it"
+ + " could have no effect.");
+ }
+ pw.println("Disabled change " + changeId + " for " + packageName + ".");
+ return 0;
+ case "reset":
+ if (config.removeOverride(changeId, packageName)) {
+ pw.println("Reset change " + changeId + " for " + packageName
+ + " to default value.");
+ } else {
+ pw.println("No override exists for changeId " + changeId + ".");
+ }
+ return 0;
+ default:
+ pw.println("Invalid toggle value: '" + toggleValue + "'.");
+ }
+ return -1;
+ }
+
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3170,6 +3217,8 @@
pw.println(" without restarting any processes.");
pw.println(" write");
pw.println(" Write all pending state to storage.");
+ pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+ pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index fea5d83..bcf1d80 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -128,20 +128,24 @@
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param changeId The ID of the change to be overridden. Note, this call will succeed even if
- * this change is not known; it will only have any affect if any code in the
+ * this change is not known; it will only have any effect if any code in the
* platform is gated on the ID given.
* @param packageName The app package name to override the change for.
* @param enabled If the change should be enabled or disabled.
+ * @return {@code true} if the change existed before adding the override.
*/
- public void addOverride(long changeId, String packageName, boolean enabled) {
+ public boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = true;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
+ alreadyKnown = false;
c = new CompatChange(changeId);
addChange(c);
}
c.addPackageOverride(packageName, enabled);
}
+ return alreadyKnown;
}
/**
@@ -151,14 +155,18 @@
*
* @param changeId The ID of the change that was overridden.
* @param packageName The app package name that was overridden.
+ * @return {@code true} if an override existed;
*/
- public void removeOverride(long changeId, String packageName) {
+ public boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c != null) {
+ overrideExists = true;
c.removePackageOverride(packageName);
}
}
+ return overrideExists;
}
}
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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 24a0c88..960cce4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -16591,13 +16591,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: "
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 a7da3ec..d3e5df5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2269,6 +2269,11 @@
return;
}
+ // Permission is already revoked, no need to do anything.
+ if (!permissionsState.hasRuntimePermission(permName, userId)) {
+ return;
+ }
+
if (permissionsState.revokeRuntimePermission(bp, userId) ==
PERMISSION_OPERATION_FAILURE) {
return;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 037293f..a569bff 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -798,7 +798,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 + ")");
@@ -811,8 +811,9 @@
* 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 +822,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/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 9cd6b0d..c6a1867 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -49,6 +49,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.provider.Telephony;
import android.service.sms.FinancialSmsService;
import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
@@ -60,6 +61,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.SmsApplication;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.CollectionUtils;
@@ -377,13 +379,16 @@
}
@Override
- public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder) {
mListenerHandler.sendMessage(PooledLambda.obtainMessage(
- RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
+ RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId,
+ removedHolder, addedHolder));
}
@WorkerThread
- private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
+ private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
if (listeners != null) {
notifyRoleHoldersChangedForListeners(listeners, roleName, userId);
@@ -394,6 +399,12 @@
if (allUsersListeners != null) {
notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId);
}
+
+ // Legacy: sms app changed broadcasts
+ if (RoleManager.ROLE_SMS.equals(roleName)) {
+ SmsApplication.broadcastSmsAppChange(getContext(), UserHandle.of(userId),
+ removedHolder, addedHolder);
+ }
}
@WorkerThread
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index c7e3fa4..6375b48 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -294,7 +294,7 @@
}
if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
+ mCallback.onRoleHoldersChanged(roleName, mUserId, null, packageName);
}
return true;
}
@@ -328,7 +328,7 @@
}
if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
+ mCallback.onRoleHoldersChanged(roleName, mUserId, packageName, null);
}
return true;
}
@@ -632,6 +632,7 @@
* @param roleName the name of the role whose holders are changed
* @param userId the user id for this role holder change
*/
- void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
+ void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId,
+ @Nullable String removedHolder, @Nullable String addedHolder);
}
}
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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0faea61..371a943 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1616,8 +1616,11 @@
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
+ // Making sure the client state is RESUMED after transaction completed and doing
+ // so only if activity is currently RESUMED. Otherwise, client may have extra
+ // life-cycle calls to RESUMED (and PAUSED later).
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- NewIntentItem.obtain(ar));
+ NewIntentItem.obtain(ar, mState == RESUMED));
unsent = false;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 30e866b..6bed462 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2964,7 +2964,8 @@
}
if (next.newIntents != null) {
- transaction.addCallback(NewIntentItem.obtain(next.newIntents));
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
}
// Well the app will no longer be stopped.
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c992a69..19916bc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -415,7 +415,7 @@
void sendErrorResult(String message) {
try {
- if (callerApp.hasThread()) {
+ if (callerApp != null && callerApp.hasThread()) {
callerApp.getThread().scheduleCrash(message);
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1659131..b837d9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -140,7 +140,7 @@
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-import static com.android.server.wm.utils.RegionUtils.forEachRect;
+import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
@@ -5142,7 +5142,7 @@
final int[] remainingLeftRight =
{mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
- // Traverse all windows bottom up to assemble the gesture exclusion rects.
+ // Traverse all windows top down to assemble the gesture exclusion rects.
// For each window, we only take the rects that fall within its touchable region.
forAllWindows(w -> {
if (w.cantReceiveTouchInput() || !w.isVisible()
@@ -5150,23 +5150,25 @@
|| unhandled.isEmpty()) {
return;
}
- final boolean modal =
- (w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
// Get the touchable region of the window, and intersect with where the screen is still
// touchable, i.e. touchable regions on top are not covering it yet.
- w.getTouchableRegion(touchableRegion);
+ w.getEffectiveTouchableRegion(touchableRegion);
touchableRegion.op(unhandled, Op.INTERSECT);
- rectListToRegion(w.getSystemGestureExclusion(), local);
+ if (w.isImplicitlyExcludingAllSystemGestures()) {
+ local.set(touchableRegion);
+ } else {
+ rectListToRegion(w.getSystemGestureExclusion(), local);
- // Transform to display coordinates
- local.scale(w.mGlobalScale);
- final Rect frame = w.getWindowFrames().mFrame;
- local.translate(frame.left, frame.top);
+ // Transform to display coordinates
+ local.scale(w.mGlobalScale);
+ final Rect frame = w.getWindowFrames().mFrame;
+ local.translate(frame.left, frame.top);
- // A window can only exclude system gestures where it is actually touchable
- local.op(touchableRegion, Op.INTERSECT);
+ // A window can only exclude system gestures where it is actually touchable
+ local.op(touchableRegion, Op.INTERSECT);
+ }
// Apply restriction if necessary.
if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
@@ -5225,13 +5227,13 @@
r.op(edge, Op.INTERSECT);
final int[] remaining = {limit};
- forEachRect(r, rect -> {
+ forEachRectReverse(r, rect -> {
if (remaining[0] <= 0) {
return;
}
final int height = rect.height();
if (height > remaining[0]) {
- rect.bottom = rect.top + remaining[0];
+ rect.top = rect.bottom - remaining[0];
}
remaining[0] -= height;
global.op(rect, Op.UNION);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 6127303..553b0ff 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -176,6 +176,8 @@
mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token);
mTransferTouchFromToken = null;
+ // syncInputWindows here to ensure the input channel isn't removed before the transfer.
+ mTransaction.syncInputWindows();
mTransaction.apply();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2db0131..fb57d73 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE;
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -844,6 +845,7 @@
boolean mWindowsChanged = false;
int mSystemGestureExclusionLimitDp;
+ boolean mSystemGestureExcludedByPreQStickyImmersive;
public interface WindowChangeListener {
public void windowsChanged();
@@ -1142,13 +1144,21 @@
mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ mSystemGestureExcludedByPreQStickyImmersive =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
new HandlerExecutor(mH), properties -> {
synchronized (mGlobalLock) {
final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
- if (mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+ if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+ || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c6c9e1b..43ad091 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -24,6 +24,8 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.Transaction;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -155,6 +157,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
+import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
@@ -657,6 +660,15 @@
return true;
}
+ boolean isImplicitlyExcludingAllSystemGestures() {
+ final int immersiveStickyFlags =
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ final boolean immersiveSticky =
+ (mSystemUiVisibility & immersiveStickyFlags) == immersiveStickyFlags;
+ return immersiveSticky && mWmService.mSystemGestureExcludedByPreQStickyImmersive
+ && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
@@ -2999,6 +3011,25 @@
subtractTouchExcludeRegionIfNeeded(outRegion);
}
+ /**
+ * Get the effective touchable region in global coordinates.
+ *
+ * In contrast to {@link #getTouchableRegion}, this takes into account
+ * {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
+ */
+ void getEffectiveTouchableRegion(Region outRegion) {
+ final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
+ final DisplayContent dc = getDisplayContent();
+
+ if (modal && dc != null) {
+ outRegion.set(dc.getBounds());
+ cropRegionToStackBoundsIfNeeded(outRegion);
+ subtractTouchExcludeRegionIfNeeded(outRegion);
+ } else {
+ getTouchableRegion(outRegion);
+ }
+ }
+
private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) {
final Task task = getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index 8cd6f88..b1b3070 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -20,6 +20,8 @@
import android.graphics.Region;
import android.graphics.RegionIterator;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -48,14 +50,21 @@
/**
* Applies actions on each rect contained within a {@code Region}.
*
+ * Order is bottom to top, then right to left.
+ *
* @param region the given region.
* @param rectConsumer the action holder.
*/
- public static void forEachRect(Region region, Consumer<Rect> rectConsumer) {
+ public static void forEachRectReverse(Region region, Consumer<Rect> rectConsumer) {
final RegionIterator it = new RegionIterator(region);
+ final ArrayList<Rect> rects = new ArrayList<>();
final Rect rect = new Rect();
while (it.next(rect)) {
- rectConsumer.accept(rect);
+ rects.add(new Rect(rect));
}
+ // TODO: instead of creating an array and reversing it, expose the reverse iterator through
+ // JNI.
+ Collections.reverse(rects);
+ rects.forEach(rectConsumer);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f49a575..7cd097e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -26,8 +26,13 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -789,6 +794,58 @@
}
@Test
+ public void testCalculateSystemGestureExclusion_modal() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
+
+ final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
+ win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ win2.getAttrs().width = 10;
+ win2.getAttrs().height = 10;
+ win2.setSystemGestureExclusion(Collections.emptyList());
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+ win2.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+ }
+
+ @Test
+ public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
+ synchronized (mWm.mGlobalLock) {
+ mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
+
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
+ SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ win.mAppToken.mTargetSdk = P;
+
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
+
+ win.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ expected.set(dc.getBounds());
+ assertEquals(expected, dc.calculateSystemGestureExclusion());
+
+ win.setHasSurface(false);
+ }
+ }
+
+ @Test
public void testOrientationChangeLogging() {
MetricsLogger mockLogger = mock(MetricsLogger.class);
Configuration oldConfig = new Configuration();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c3561f4..d034f27 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -49,6 +49,7 @@
int mRotationToReport = 0;
boolean mKeyguardShowingAndNotOccluded = false;
+ boolean mOkToAnimate = true;
private Runnable mRunnableWhenAddingSplashScreen;
@@ -222,7 +223,7 @@
@Override
public boolean okToAnimate() {
- return true;
+ return mOkToAnimate;
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 44dc24b..98f52cb 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.Manifest.permission;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
@@ -662,49 +663,69 @@
}
defaultSmsAppChanged(context);
+ }
+ }
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
- }
- if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
- // Notify the old sms app that it's no longer the default
- final Intent oldAppIntent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(oldAppData.mPackageName,
- oldAppData.mSmsAppChangedReceiverClass);
- oldAppIntent.setComponent(component);
- oldAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
- }
- context.sendBroadcastAsUser(oldAppIntent, userHandle);
- }
- // Notify the new sms app that it's now the default (if the new sms app has a receiver
- // to handle the changed default sms intent).
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
- applicationData);
- }
- if (applicationData.mSmsAppChangedReceiverClass != null) {
- final Intent intent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(applicationData.mPackageName,
- applicationData.mSmsAppChangedReceiverClass);
- intent.setComponent(component);
- intent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName);
- }
- context.sendBroadcastAsUser(intent, userHandle);
- }
+ /**
+ * Sends broadcasts on sms app change:
+ * {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED}
+ * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ */
+ public static void broadcastSmsAppChange(Context context,
+ UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) {
+ Collection<SmsApplicationData> apps = getApplicationCollection(context);
- // Send an implicit broadcast for the system server.
- // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ broadcastSmsAppChange(context, userHandle,
+ getApplicationForPackage(apps, oldPackage),
+ getApplicationForPackage(apps, newPackage));
+ }
+
+ private static void broadcastSmsAppChange(Context context, UserHandle userHandle,
+ @Nullable SmsApplicationData oldAppData,
+ @Nullable SmsApplicationData applicationData) {
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
+ }
+ if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
+ // Notify the old sms app that it's no longer the default
+ final Intent oldAppIntent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(oldAppData.mPackageName,
+ oldAppData.mSmsAppChangedReceiverClass);
+ oldAppIntent.setComponent(component);
+ oldAppIntent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
+ }
+ context.sendBroadcastAsUser(oldAppIntent, userHandle);
+ }
+ // Notify the new sms app that it's now the default (if the new sms app has a receiver
+ // to handle the changed default sms intent).
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
+ applicationData);
+ }
+ if (applicationData != null && applicationData.mSmsAppChangedReceiverClass != null) {
final Intent intent =
- new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
- context.sendBroadcastAsUser(intent, userHandle,
- permission.MONITOR_DEFAULT_SMS_PACKAGE);
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(applicationData.mPackageName,
+ applicationData.mSmsAppChangedReceiverClass);
+ intent.setComponent(component);
+ intent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + applicationData.mPackageName);
+ }
+ context.sendBroadcastAsUser(intent, userHandle);
+ }
+ // Send an implicit broadcast for the system server.
+ // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ final Intent intent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ context.sendBroadcastAsUser(intent, userHandle,
+ permission.MONITOR_DEFAULT_SMS_PACKAGE);
+
+ if (applicationData != null) {
MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
applicationData.mPackageName);
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index beff0c6..1b002ca 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -960,6 +960,7 @@
}
@Test
+ @Ignore("b/136605788")
public void testEnableRollbackTimeoutFailsRollback() throws Exception {
try {
RollbackTestUtils.adoptShellPermissionIdentity(
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