Merge "Make sure to call the original reply-to receiver when..."
diff --git a/api/current.txt b/api/current.txt
index f1243a6..9c443f8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1005,7 +1005,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
- field public static final int primaryContentAlpha = 16843367; // 0x1010267
+ field public static final int primaryContentAlpha = 16844117; // 0x1010555
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -1061,7 +1061,9 @@
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
+ field public static final int requiredFeature = 16844119; // 0x1010557
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+ field public static final int requiredNotFeature = 16844120; // 0x1010558
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1130,7 +1132,7 @@
field public static final int searchSuggestSelection = 16843224; // 0x10101d8
field public static final int searchSuggestThreshold = 16843373; // 0x101026d
field public static final int searchViewStyle = 16843904; // 0x1010480
- field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+ field public static final int secondaryContentAlpha = 16844118; // 0x1010556
field public static final int secondaryProgress = 16843064; // 0x1010138
field public static final int secondaryProgressTint = 16843879; // 0x1010467
field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -3909,8 +3911,9 @@
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
- field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
- field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+ field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+ field public static final int IMPORTANCE_CACHED = 400; // 0x190
+ field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5482,6 +5485,7 @@
public final class NotificationChannel implements android.os.Parcelable {
ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+ ctor public NotificationChannel(java.lang.String, int, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public boolean canShowBadge();
@@ -5495,6 +5499,7 @@
method public int getLightColor();
method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
+ method public int getNameResId();
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
method public void setBypassDnd(boolean);
@@ -10478,8 +10483,11 @@
field public static final int GET_SIGNATURES = 64; // 0x40
field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+ field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+ field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
field public static final int INSTALL_REASON_POLICY = 1; // 0x1
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+ field public static final int INSTALL_REASON_USER = 4; // 0x4
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -36413,7 +36421,6 @@
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
field public static final int PURPOSE_VERIFY = 8; // 0x8
- field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
}
@@ -36924,6 +36931,7 @@
method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+ field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index caccb0f..eb237fae 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1117,7 +1117,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
- field public static final int primaryContentAlpha = 16843367; // 0x1010267
+ field public static final int primaryContentAlpha = 16844117; // 0x1010555
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -1173,7 +1173,9 @@
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
+ field public static final int requiredFeature = 16844119; // 0x1010557
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+ field public static final int requiredNotFeature = 16844120; // 0x1010558
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1246,7 +1248,7 @@
field public static final int searchSuggestSelection = 16843224; // 0x10101d8
field public static final int searchSuggestThreshold = 16843373; // 0x101026d
field public static final int searchViewStyle = 16843904; // 0x1010480
- field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+ field public static final int secondaryContentAlpha = 16844118; // 0x1010556
field public static final int secondaryProgress = 16843064; // 0x1010138
field public static final int secondaryProgressTint = 16843879; // 0x1010467
field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -4047,8 +4049,9 @@
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
- field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
- field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+ field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+ field public static final int IMPORTANCE_CACHED = 400; // 0x190
+ field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5658,6 +5661,7 @@
public final class NotificationChannel implements android.os.Parcelable {
ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+ ctor public NotificationChannel(java.lang.String, int, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public boolean canShowBadge();
@@ -5671,6 +5675,7 @@
method public int getLightColor();
method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
+ method public int getNameResId();
method public android.net.Uri getSound();
method public int getUserLockedFields();
method public long[] getVibrationPattern();
@@ -11080,8 +11085,11 @@
field public static final int INSTALL_PARSE_FAILED_NOT_APK = -100; // 0xffffff9c
field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
+ field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+ field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
field public static final int INSTALL_REASON_POLICY = 1; // 0x1
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+ field public static final int INSTALL_REASON_USER = 4; // 0x4
field public static final int INSTALL_SUCCEEDED = 1; // 0x1
field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
@@ -39336,7 +39344,6 @@
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
field public static final int PURPOSE_VERIFY = 8; // 0x8
- field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
}
@@ -39921,6 +39928,7 @@
method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+ field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 81718d5..ccf688c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1005,7 +1005,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
- field public static final int primaryContentAlpha = 16843367; // 0x1010267
+ field public static final int primaryContentAlpha = 16844117; // 0x1010555
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -1061,7 +1061,9 @@
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
+ field public static final int requiredFeature = 16844119; // 0x1010557
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+ field public static final int requiredNotFeature = 16844120; // 0x1010558
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1130,7 +1132,7 @@
field public static final int searchSuggestSelection = 16843224; // 0x10101d8
field public static final int searchSuggestThreshold = 16843373; // 0x101026d
field public static final int searchViewStyle = 16843904; // 0x1010480
- field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+ field public static final int secondaryContentAlpha = 16844118; // 0x1010556
field public static final int secondaryProgress = 16843064; // 0x1010138
field public static final int secondaryProgressTint = 16843879; // 0x1010467
field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -3918,8 +3920,9 @@
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
- field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
- field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+ field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+ field public static final int IMPORTANCE_CACHED = 400; // 0x190
+ field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5492,6 +5495,7 @@
public final class NotificationChannel implements android.os.Parcelable {
ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+ ctor public NotificationChannel(java.lang.String, int, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public boolean canShowBadge();
@@ -5505,6 +5509,7 @@
method public int getLightColor();
method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
+ method public int getNameResId();
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
method public void setBypassDnd(boolean);
@@ -10513,8 +10518,11 @@
field public static final int GET_SIGNATURES = 64; // 0x40
field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+ field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+ field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
field public static final int INSTALL_REASON_POLICY = 1; // 0x1
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+ field public static final int INSTALL_REASON_USER = 4; // 0x4
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -36552,7 +36560,6 @@
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
field public static final int PURPOSE_VERIFY = 8; // 0x8
- field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
}
@@ -37108,6 +37115,7 @@
method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+ field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fda9966..cae37c6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3024,14 +3024,22 @@
/**
* Constant for {@link #importance}: This process process contains
- * background code that is expendable.
+ * cached code that is expendable, not actively running any app components
+ * we care about.
*/
- public static final int IMPORTANCE_BACKGROUND = 400;
+ public static final int IMPORTANCE_CACHED = 400;
+
+ /**
+ * @deprecated Renamed to {@link #IMPORTANCE_CACHED}.
+ */
+ public static final int IMPORTANCE_BACKGROUND = IMPORTANCE_CACHED;
/**
* Constant for {@link #importance}: This process is empty of any
* actively running code.
+ * @deprecated This value is no longer reported, use {@link #IMPORTANCE_CACHED} instead.
*/
+ @Deprecated
public static final int IMPORTANCE_EMPTY = 500;
/**
@@ -3044,7 +3052,7 @@
if (procState == PROCESS_STATE_NONEXISTENT) {
return IMPORTANCE_GONE;
} else if (procState >= PROCESS_STATE_HOME) {
- return IMPORTANCE_BACKGROUND;
+ return IMPORTANCE_CACHED;
} else if (procState >= PROCESS_STATE_SERVICE) {
return IMPORTANCE_SERVICE;
} else if (procState > PROCESS_STATE_HEAVY_WEIGHT) {
@@ -3066,7 +3074,7 @@
public static int importanceToProcState(int importance) {
if (importance == IMPORTANCE_GONE) {
return PROCESS_STATE_NONEXISTENT;
- } else if (importance >= IMPORTANCE_BACKGROUND) {
+ } else if (importance >= IMPORTANCE_CACHED) {
return PROCESS_STATE_HOME;
} else if (importance >= IMPORTANCE_SERVICE) {
return PROCESS_STATE_SERVICE;
@@ -3088,8 +3096,8 @@
/**
* The relative importance level that the system places on this
* process. May be one of {@link #IMPORTANCE_FOREGROUND},
- * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE},
- * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These
+ * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE}, or
+ * {@link #IMPORTANCE_CACHED}. These
* constants are numbered so that "more important" values are always
* smaller than "less important" values.
*/
@@ -3101,7 +3109,7 @@
* utility of processes within a category. This number means nothing
* except that a smaller values are more recently used (and thus
* more important). Currently an LRU value is only maintained for
- * the {@link #IMPORTANCE_BACKGROUND} category, though others may
+ * the {@link #IMPORTANCE_CACHED} category, though others may
* be maintained in the future.
*/
public int lru;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7ee93d0..0ab4b80 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2254,11 +2254,10 @@
}
void setResources(Resources r) {
- if (mPackageInfo.getTargetSdkVersion() < VERSION_CODES.O) {
- mResources = new CompatResources(r, this);
- } else {
- mResources = r;
+ if (r instanceof CompatResources) {
+ ((CompatResources) r).setContext(this);
}
+ mResources = r;
}
void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 1a51608..85e6b85 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -20,8 +20,10 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.SystemApi;
-import android.graphics.Color;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Parcel;
@@ -45,6 +47,7 @@
private static final String TAG_CHANNEL = "channel";
private static final String ATT_NAME = "name";
+ private static final String ATT_NAME_RES_ID = "name_res_id";
private static final String ATT_ID = "id";
private static final String ATT_DELETED = "deleted";
private static final String ATT_PRIORITY = "priority";
@@ -138,6 +141,7 @@
private final String mId;
private CharSequence mName;
+ private int mNameResId = 0;
private int mImportance = DEFAULT_IMPORTANCE;
private boolean mBypassDnd;
private int mLockscreenVisibility = DEFAULT_VISIBILITY;
@@ -156,7 +160,9 @@
* Creates a notification channel.
*
* @param id The id of the channel. Must be unique per package.
- * @param name The user visible name of the channel.
+ * @param name The user visible name of the channel. Unchangeable once created; use this
+ * constructor if the channel represents a user-defined category that does not
+ * need to be translated.
* @param importance The importance of the channel. This controls how interruptive notifications
* posted to this channel are. See e.g.
* {@link NotificationManager#IMPORTANCE_DEFAULT}.
@@ -167,6 +173,21 @@
this.mImportance = importance;
}
+ /**
+ * Creates a notification channel.
+ *
+ * @param id The id of the channel. Must be unique per package.
+ * @param nameResId The resource id of the string containing the channel name.
+ * @param importance The importance of the channel. This controls how interruptive notifications
+ * posted to this channel are. See e.g.
+ * {@link NotificationManager#IMPORTANCE_DEFAULT}.
+ */
+ public NotificationChannel(String id, @StringRes int nameResId, int importance) {
+ this.mId = id;
+ this.mNameResId = nameResId;
+ this.mImportance = importance;
+ }
+
protected NotificationChannel(Parcel in) {
if (in.readByte() != 0) {
mId = in.readString();
@@ -174,6 +195,7 @@
mId = null;
}
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mNameResId = in.readInt();
mImportance = in.readInt();
mBypassDnd = in.readByte() != 0;
mLockscreenVisibility = in.readInt();
@@ -206,6 +228,7 @@
dest.writeByte((byte) 0);
}
TextUtils.writeToParcel(mName, dest, flags);
+ dest.writeInt(mNameResId);
dest.writeInt(mImportance);
dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
dest.writeInt(mLockscreenVisibility);
@@ -382,11 +405,18 @@
/**
* Returns the user visible name of this channel.
*/
- public CharSequence getName() {
+ public @Nullable CharSequence getName() {
return mName;
}
/**
+ * Returns the resource id of the user visible name of this channel.
+ */
+ public int getNameResId() {
+ return mNameResId;
+ }
+
+ /**
* Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for
* notifications posted to this channel.
*/
@@ -516,7 +546,10 @@
public void writeXml(XmlSerializer out) throws IOException {
out.startTag(null, TAG_CHANNEL);
out.attribute(null, ATT_ID, getId());
- out.attribute(null, ATT_NAME, getName().toString());
+ if (getName() != null) {
+ out.attribute(null, ATT_NAME, getName().toString());
+ }
+ out.attribute(null, ATT_NAME_RES_ID, Integer.toString(getNameResId()));
if (getImportance() != DEFAULT_IMPORTANCE) {
out.attribute(
null, ATT_IMPORTANCE, Integer.toString(getImportance()));
@@ -574,6 +607,7 @@
JSONObject record = new JSONObject();
record.put(ATT_ID, getId());
record.put(ATT_NAME, getName());
+ record.put(ATT_NAME_RES_ID, getNameResId());
if (getImportance() != DEFAULT_IMPORTANCE) {
record.put(ATT_IMPORTANCE,
NotificationListenerService.Ranking.importanceToString(getImportance()));
@@ -691,6 +725,7 @@
NotificationChannel that = (NotificationChannel) o;
+ if (getNameResId() != that.getNameResId()) return false;
if (getImportance() != that.getImportance()) return false;
if (mBypassDnd != that.mBypassDnd) return false;
if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
@@ -720,6 +755,7 @@
public int hashCode() {
int result = getId() != null ? getId().hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+ result = 31 * result + getNameResId();
result = 31 * result + getImportance();
result = 31 * result + (mBypassDnd ? 1 : 0);
result = 31 * result + getLockscreenVisibility();
@@ -741,6 +777,7 @@
return "NotificationChannel{" +
"mId='" + mId + '\'' +
", mName=" + mName +
+ ", mNameResId=" + mNameResId +
", mImportance=" + mImportance +
", mBypassDnd=" + mBypassDnd +
", mLockscreenVisibility=" + mLockscreenVisibility +
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 52ec045..b42df5e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
+import android.content.res.CompatResources;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -447,7 +448,8 @@
* or the class loader is different.
*/
private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
- @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
+ @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
+ @NonNull CompatibilityInfo compatInfo) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
@@ -466,7 +468,8 @@
}
}
- Resources resources = new Resources(classLoader);
+ Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
+ : new Resources(classLoader);
resources.setImpl(impl);
activityResources.activityResources.add(new WeakReference<>(resources));
if (DEBUG) {
@@ -481,7 +484,7 @@
* otherwise creates a new Resources object.
*/
private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
- @NonNull ResourcesImpl impl) {
+ @NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
// Find an existing Resources that has this ResourcesImpl set.
final int refCount = mResourceReferences.size();
for (int i = 0; i < refCount; i++) {
@@ -498,7 +501,8 @@
}
// Create a new Resources reference and use the existing ResourcesImpl object.
- Resources resources = new Resources(classLoader);
+ Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
+ : new Resources(classLoader);
resources.setImpl(impl);
mResourceReferences.add(new WeakReference<>(resources));
if (DEBUG) {
@@ -614,7 +618,7 @@
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
}
return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl);
+ resourcesImpl, key.mCompatInfo);
}
// We will create the ResourcesImpl object outside of holding this lock.
@@ -629,7 +633,7 @@
if (DEBUG) {
Slog.d(TAG, "- using existing impl=" + resourcesImpl);
}
- return getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
// We will create the ResourcesImpl object outside of holding this lock.
@@ -659,9 +663,9 @@
final Resources resources;
if (activityToken != null) {
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl);
+ resourcesImpl, key.mCompatInfo);
} else {
- resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
return resources;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9fda3cd..b3b5bcf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -745,7 +745,8 @@
public static final int DONT_KILL_APP = 0x00000001;
/** @hide */
- @IntDef({INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY})
+ @IntDef({INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY, INSTALL_REASON_DEVICE_RESTORE,
+ INSTALL_REASON_DEVICE_SETUP, INSTALL_REASON_USER})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallReason {}
@@ -760,6 +761,21 @@
public static final int INSTALL_REASON_POLICY = 1;
/**
+ * Code indicating that this package was installed as part of restoring from another device.
+ */
+ public static final int INSTALL_REASON_DEVICE_RESTORE = 2;
+
+ /**
+ * Code indicating that this package was installed as part of device setup.
+ */
+ public static final int INSTALL_REASON_DEVICE_SETUP = 3;
+
+ /**
+ * Code indicating that the package installation was initiated by the user.
+ */
+ public static final int INSTALL_REASON_USER = 4;
+
+ /**
* Installation return code: this is passed to the
* {@link IPackageInstallObserver} on success.
*
@@ -5000,6 +5016,7 @@
*/
public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) {
final PackageParser parser = new PackageParser();
+ parser.setCallback(new PackageParser.CallbackImpl(this));
final File apkFile = new File(archiveFilePath);
try {
if ((flags & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
@@ -6125,6 +6142,9 @@
*
* @see #INSTALL_REASON_UNKNOWN
* @see #INSTALL_REASON_POLICY
+ * @see #INSTALL_REASON_DEVICE_RESTORE
+ * @see #INSTALL_REASON_DEVICE_SETUP
+ * @see #INSTALL_REASON_USER
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f801e45..60cc6b0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -285,6 +285,7 @@
private String[] mSeparateProcesses;
private boolean mOnlyCoreApps;
private DisplayMetrics mMetrics;
+ private Callback mCallback;
private File mCacheDir;
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
@@ -506,6 +507,37 @@
mCacheDir = cacheDir;
}
+ /**
+ * Callback interface for retrieving information that may be needed while parsing
+ * a package.
+ */
+ public interface Callback {
+ boolean hasFeature(String feature);
+ }
+
+ /**
+ * Standard implementation of {@link Callback} on top of the public {@link PackageManager}
+ * class.
+ */
+ public static final class CallbackImpl implements Callback {
+ private final PackageManager mPm;
+
+ public CallbackImpl(PackageManager pm) {
+ mPm = pm;
+ }
+
+ @Override public boolean hasFeature(String feature) {
+ return mPm.hasSystemFeature(feature);
+ }
+ }
+
+ /**
+ * Set the {@link Callback} that can be used while parsing.
+ */
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
public static final boolean isApkFile(File file) {
return isApkPath(file.getName());
}
@@ -2079,15 +2111,15 @@
return null;
}
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {
- if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
+ if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION)) {
- if (parsePermission(pkg, res, parser, outError) == null) {
+ if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) {
- if (parsePermissionTree(pkg, res, parser, outError) == null) {
+ if (!parsePermissionTree(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
@@ -2708,22 +2740,44 @@
}
}
+ final String requiredFeature = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+ final String requiredNotfeature = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+
sa.recycle();
- if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) {
- if (name != null) {
- int index = pkg.requestedPermissions.indexOf(name);
- if (index == -1) {
- pkg.requestedPermissions.add(name.intern());
- } else {
- Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
- + name + " in package: " + pkg.packageName + " at: "
- + parser.getPositionDescription());
- }
- }
+ XmlUtils.skipCurrentTag(parser);
+
+ if (name == null) {
+ return true;
}
- XmlUtils.skipCurrentTag(parser);
+ if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+ return true;
+ }
+
+ // Only allow requesting this permission if the platform supports the given feature.
+ if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) {
+ return true;
+ }
+
+ // Only allow requesting this permission if the platform doesn't support the given feature.
+ if (requiredNotfeature != null && mCallback != null
+ && mCallback.hasFeature(requiredNotfeature)) {
+ return true;
+ }
+
+ int index = pkg.requestedPermissions.indexOf(name);
+ if (index == -1) {
+ pkg.requestedPermissions.add(name.intern());
+ } else {
+ Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+ + name + " in package: " + pkg.packageName + " at: "
+ + parser.getPositionDescription());
+ }
+
return true;
}
@@ -2951,7 +3005,7 @@
return true;
}
- private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
+ private boolean parsePermissionGroup(Package owner, int flags, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
PermissionGroup perm = new PermissionGroup(owner);
@@ -2968,7 +3022,7 @@
com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
perm.info.descriptionRes = sa.getResourceId(
@@ -2987,22 +3041,22 @@
if (!parseAllMetaData(res, parser, "<permission-group>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
owner.permissionGroups.add(perm);
- return perm;
+ return true;
}
- private Permission parsePermission(Package owner, Resources res,
+ private boolean parsePermission(Package owner, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
- Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermission);
+ Permission perm = new Permission(owner);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission>", sa, true /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestPermission_name,
@@ -3013,7 +3067,7 @@
com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
// Note: don't allow this value to be a reference to a resource
@@ -3040,7 +3094,7 @@
if (perm.info.protectionLevel == -1) {
outError[0] = "<permission> does not specify protectionLevel";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
@@ -3052,21 +3106,21 @@
outError[0] = "<permission> protectionLevel specifies a non-ephemeral flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
}
if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
owner.permissions.add(perm);
- return perm;
+ return true;
}
- private Permission parsePermissionTree(Package owner, Resources res,
+ private boolean parsePermissionTree(Package owner, Resources res,
XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
@@ -3084,7 +3138,7 @@
com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
sa.recycle();
@@ -3097,7 +3151,7 @@
outError[0] = "<permission-tree> name has less than three segments: "
+ perm.info.name;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
perm.info.descriptionRes = 0;
@@ -3107,12 +3161,12 @@
if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return null;
+ return false;
}
owner.permissions.add(perm);
- return perm;
+ return true;
}
private Instrumentation parseInstrumentation(Package owner, Resources res,
diff --git a/core/java/android/content/res/CompatResources.java b/core/java/android/content/res/CompatResources.java
index 15575fd..829b6b7 100644
--- a/core/java/android/content/res/CompatResources.java
+++ b/core/java/android/content/res/CompatResources.java
@@ -27,11 +27,17 @@
*/
public class CompatResources extends Resources {
- private final WeakReference<Context> mContext;
+ private WeakReference<Context> mContext;
- public CompatResources(Resources base, Context context) {
- super(base.getClassLoader());
- setImpl(base.getImpl());
+ public CompatResources(ClassLoader cls) {
+ super(cls);
+ mContext = new WeakReference<>(null);
+ }
+
+ /**
+ * @hide
+ */
+ public void setContext(Context context) {
mContext = new WeakReference<>(context);
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index da35ee9..781e235 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -21,6 +21,8 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
@@ -78,6 +80,11 @@
private static final int NEEDS_SCREEN_COMPAT = 8;
/**
+ * Set if the application needs to run in with compat resources.
+ */
+ private static final int NEEDS_COMPAT_RES = 16;
+
+ /**
* The effective screen density we have selected for this application.
*/
public final int applicationDensity;
@@ -96,6 +103,9 @@
boolean forceCompat) {
int compatFlags = 0;
+ if (appInfo.targetSdkVersion < VERSION_CODES.O) {
+ compatFlags |= NEEDS_COMPAT_RES;
+ }
if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
|| appInfo.largestWidthLimitDp != 0) {
// New style screen requirements spec.
@@ -274,6 +284,10 @@
return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
}
+ public boolean needsCompatResources() {
+ return (mCompatibilityFlags&NEEDS_COMPAT_RES) != 0;
+ }
+
/**
* Returns the translator which translates the coordinates in compatibility mode.
* @param params the window's parameter
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index b9e4bad..caea202 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -506,7 +506,7 @@
}
/**
- * Detect unbuffered input/output operations.
+ * Disable detection of unbuffered input/output operations.
*/
public Builder permitUnbufferedIo() {
return disable(DETECT_UNBUFFERED_IO);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 78820b5..b525193 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -35,7 +35,6 @@
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
- public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@@ -82,12 +81,8 @@
/**
* Get the value for the given key.
* @return an empty string if the key isn't found
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static String get(String key) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
@@ -95,12 +90,8 @@
/**
* Get the value for the given key.
* @return if the key isn't found, return def if it isn't null, or an empty string otherwise
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static String get(String key, String def) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
@@ -111,12 +102,8 @@
* @param def a default value to return
* @return the key parsed as an integer, or def if the key isn't found or
* cannot be parsed
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static int getInt(String key, int def) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_int(key, def);
}
@@ -127,12 +114,8 @@
* @param def a default value to return
* @return the key parsed as a long, or def if the key isn't found or
* cannot be parsed
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static long getLong(String key, long def) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_long(key, def);
}
@@ -148,25 +131,17 @@
* @param def a default value to return
* @return the key parsed as a boolean, or def if the key isn't found or is
* not able to be parsed as a boolean.
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static boolean getBoolean(String key, boolean def) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_boolean(key, def);
}
/**
* Set the value for the given key.
- * @throws IllegalArgumentException if the key exceeds 32 characters
* @throws IllegalArgumentException if the value exceeds 92 characters
*/
public static void set(String key, String val) {
- if (key.length() > PROP_NAME_MAX) {
- throw newKeyTooLargeException(key);
- }
if (val != null && val.length() > PROP_VALUE_MAX) {
throw newValueTooLargeException(key, val);
}
@@ -196,11 +171,6 @@
}
}
- private static IllegalArgumentException newKeyTooLargeException(String key) {
- return new IllegalArgumentException("system property key '" + key + "' is longer than "
- + PROP_NAME_MAX + " characters");
- }
-
private static IllegalArgumentException newValueTooLargeException(String key, String value) {
return new IllegalArgumentException("value of system property '" + key + "' is longer than "
+ PROP_VALUE_MAX + " characters: " + value);
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 5461e6b..ad8ad69 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -138,8 +138,6 @@
public static final int KM_PURPOSE_DECRYPT = 1;
public static final int KM_PURPOSE_SIGN = 2;
public static final int KM_PURPOSE_VERIFY = 3;
- public static final int KM_PURPOSE_DERIVE_KEY = 4;
- public static final int KM_PURPOSE_WRAP_KEY = 5;
// Key formats.
public static final int KM_KEY_FORMAT_X509 = 0;
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 1781c2a..2116847 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -126,7 +126,9 @@
public static final String EXTRA_TOKEN = "token";
/**
- * @hide
+ * May be included when {@link #ACTION_QS_TILE_PREFERENCES} is launched from a tile.
+ * This extra may contain the {@link ComponentName} of the tile that triggered
+ * the preferences to be opened.
*/
public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0ac16c1..b718696 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -95,11 +95,6 @@
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long nativeObject,
IBinder handle, long frame);
- private static native void nativeDeferTransactionUntilSurface(long nativeObject,
- long surfaceObject, long frame);
- private static native void nativeReparentChildren(long nativeObject,
- IBinder handle);
- private static native void nativeSeverChildren(long nativeObject);
private static native void nativeSetOverrideScalingMode(long nativeObject,
int scalingMode);
private static native IBinder nativeGetHandle(long nativeObject);
@@ -426,18 +421,6 @@
nativeDeferTransactionUntil(mNativeObject, handle, frame);
}
- public void deferTransactionUntil(Surface barrier, long frame) {
- nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
- }
-
- public void reparentChildren(IBinder newParentHandle) {
- nativeReparentChildren(mNativeObject, newParentHandle);
- }
-
- public void detachChildren() {
- nativeSeverChildren(mNativeObject);
- }
-
public void setOverrideScalingMode(int scalingMode) {
checkNotReleased();
nativeSetOverrideScalingMode(mNativeObject, scalingMode);
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index b5912bc..3cf5af4 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -27,7 +27,6 @@
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
- private static native long nativeCreateScoped(long surfacePtr);
private static native void nativeDestroy(long ptr);
private static native void nativeKill(long ptr);
@@ -36,10 +35,6 @@
mNativeClient = nativeCreate();
}
- public SurfaceSession(Surface root) {
- mNativeClient = nativeCreateScoped(root.mNativeObject);
- }
-
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6430633..d2577d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,10 +16,6 @@
package android.view;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
-
import android.content.Context;
import android.content.res.CompatibilityInfo.Translator;
import android.content.res.Configuration;
@@ -30,12 +26,16 @@
import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import com.android.internal.view.BaseIWindow;
import com.android.internal.view.SurfaceCallbackHelper;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
@@ -92,8 +92,8 @@
* positioned asynchronously.</p>
*/
public class SurfaceView extends View {
- private static final String TAG = "SurfaceView";
- private static final boolean DEBUG = false;
+ static private final String TAG = "SurfaceView";
+ static private final boolean DEBUG = false;
final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
@@ -102,23 +102,28 @@
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface(); // Current surface in use
+ final Surface mNewSurface = new Surface(); // New surface we are switching to
boolean mDrawingStopped = true;
- // We use this to track if the application has produced a frame
- // in to the Surface. Up until that point, we should be careful not to punch
- // holes.
- boolean mDrawFinished = false;
- final Rect mScreenRect = new Rect();
- SurfaceSession mSurfaceSession;
-
- SurfaceControl mSurfaceControl;
+ final WindowManager.LayoutParams mLayout
+ = new WindowManager.LayoutParams();
+ IWindowSession mSession;
+ MyWindow mWindow;
+ final Rect mVisibleInsets = new Rect();
+ final Rect mWinFrame = new Rect();
+ final Rect mOverscanInsets = new Rect();
+ final Rect mContentInsets = new Rect();
+ final Rect mStableInsets = new Rect();
+ final Rect mOutsets = new Rect();
+ final Rect mBackdropFrame = new Rect();
final Rect mTmpRect = new Rect();
final Configuration mConfiguration = new Configuration();
static final int KEEP_SCREEN_ON_MSG = 1;
- static final int DRAW_FINISHED_MSG = 2;
+ static final int GET_NEW_SURFACE_MSG = 2;
+ static final int UPDATE_WINDOW_MSG = 3;
- int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
boolean mIsCreating = false;
private volatile boolean mRtHandlingPositionUpdates = false;
@@ -130,9 +135,11 @@
case KEEP_SCREEN_ON_MSG: {
setKeepScreenOn(msg.arg1 != 0);
} break;
- case DRAW_FINISHED_MSG: {
- mDrawFinished = true;
- invalidate();
+ case GET_NEW_SURFACE_MSG: {
+ handleGetNewSurface();
+ } break;
+ case UPDATE_WINDOW_MSG: {
+ updateWindow();
} break;
}
}
@@ -142,7 +149,7 @@
= new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
- updateSurface();
+ updateWindow();
}
};
@@ -152,14 +159,13 @@
public boolean onPreDraw() {
// reposition ourselves where the surface is
mHaveFrame = getWidth() > 0 && getHeight() > 0;
- updateSurface();
+ updateWindow();
return true;
}
};
boolean mRequestedVisible = false;
boolean mWindowVisibility = false;
- boolean mLastWindowVisibility = false;
boolean mViewVisibility = false;
int mRequestedWidth = -1;
int mRequestedHeight = -1;
@@ -175,17 +181,19 @@
boolean mVisible = false;
int mWindowSpaceLeft = -1;
int mWindowSpaceTop = -1;
- int mSurfaceWidth = -1;
- int mSurfaceHeight = -1;
+ int mWindowSpaceWidth = -1;
+ int mWindowSpaceHeight = -1;
int mFormat = -1;
final Rect mSurfaceFrame = new Rect();
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+ boolean mUpdateWindowNeeded;
+ boolean mReportDrawNeeded;
private Translator mTranslator;
+ private int mWindowInsetLeft;
+ private int mWindowInsetTop;
private boolean mGlobalListenersAdded;
- private int mSurfaceFlags = SurfaceControl.HIDDEN;
-
public SurfaceView(Context context) {
this(context, null);
}
@@ -219,8 +227,11 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
+ mSession = getWindowSession();
+ mLayout.token = getWindowToken();
+ mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
+ mLayout.packageName = mContext.getOpPackageName();
mViewVisibility = getVisibility() == VISIBLE;
- mRequestedVisible = mViewVisibility && mWindowVisibility;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
@@ -235,7 +246,7 @@
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
- updateSurface();
+ updateWindow();
}
@Override
@@ -253,7 +264,7 @@
requestLayout();
}
mRequestedVisible = newRequestedVisible;
- updateSurface();
+ updateWindow();
}
@Override
@@ -266,14 +277,19 @@
}
mRequestedVisible = false;
-
- updateSurface();
- if (mSurfaceControl != null) {
- mSurfaceControl.destroy();
- }
- mSurfaceControl = null;
-
+ updateWindow();
mHaveFrame = false;
+ if (mWindow != null) {
+ try {
+ mSession.remove(mWindow);
+ } catch (RemoteException ex) {
+ // Not much we can do here...
+ }
+ mWindow = null;
+ }
+ mSession = null;
+ mLayout.token = null;
+
super.onDetachedFromWindow();
}
@@ -292,13 +308,13 @@
@Override
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean result = super.setFrame(left, top, right, bottom);
- updateSurface();
+ updateWindow();
return result;
}
@Override
public boolean gatherTransparentRegion(Region region) {
- if (isAboveParent()) {
+ if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
return super.gatherTransparentRegion(region);
}
@@ -325,7 +341,7 @@
@Override
public void draw(Canvas canvas) {
- if (mDrawFinished && !isAboveParent()) {
+ if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
// punch a whole in the view-hierarchy below us
@@ -337,8 +353,8 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- if (mDrawFinished && !isAboveParent()) {
- // draw() is not called when SKIP_DRAW is set
+ if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+ // if SKIP_DRAW is cleared, draw() has already punched a hole
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
@@ -359,8 +375,9 @@
* <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
*/
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
- mSubLayer = isMediaOverlay
- ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
+ mWindowType = isMediaOverlay
+ ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
}
/**
@@ -378,9 +395,12 @@
*/
public void setZOrderOnTop(boolean onTop) {
if (onTop) {
- mSubLayer = APPLICATION_PANEL_SUBLAYER;
+ mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ // ensures the surface is placed below the IME
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
- mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+ mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
}
@@ -398,23 +418,31 @@
*/
public void setSecure(boolean isSecure) {
if (isSecure) {
- mSurfaceFlags |= SurfaceControl.SECURE;
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
} else {
- mSurfaceFlags &= ~SurfaceControl.SECURE;
+ mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
}
}
+ /**
+ * Hack to allow special layering of windows. The type is one of the
+ * types in WindowManager.LayoutParams. This is a hack so:
+ * @hide
+ */
+ public void setWindowType(int type) {
+ mWindowType = type;
+ }
+
/** @hide */
- protected void updateSurface() {
+ protected void updateWindow() {
if (!mHaveFrame) {
return;
}
ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
- return;
+ if (viewRoot != null) {
+ mTranslator = viewRoot.mTranslator;
}
- mTranslator = viewRoot.mTranslator;
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
}
@@ -424,15 +452,17 @@
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
+ final boolean creating = mWindow == null;
final boolean formatChanged = mFormat != mRequestedFormat;
- final boolean creating = (mSurfaceControl == null || formatChanged)
- && mRequestedVisible;
- final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+ final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
- final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+ final boolean layoutSizeChanged = getWidth() != mLayout.width
+ || getHeight() != mLayout.height;
+
boolean redrawNeeded = false;
- if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || mUpdateWindowNeeded || mReportDrawNeeded) {
getLocationInWindow(mLocation);
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
@@ -446,74 +476,93 @@
final boolean visible = mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
- mSurfaceWidth = myWidth;
- mSurfaceHeight = myHeight;
+ mWindowSpaceWidth = myWidth;
+ mWindowSpaceHeight = myHeight;
mFormat = mRequestedFormat;
- mLastWindowVisibility = mWindowVisibility;
- mScreenRect.left = mWindowSpaceLeft;
- mScreenRect.top = mWindowSpaceTop;
- mScreenRect.right = mWindowSpaceLeft + getWidth();
- mScreenRect.bottom = mWindowSpaceTop + getHeight();
+ // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+
+ // Places the window relative
+ mLayout.x = mWindowSpaceLeft;
+ mLayout.y = mWindowSpaceTop;
+ mLayout.width = getWidth();
+ mLayout.height = getHeight();
if (mTranslator != null) {
- mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
}
- if (creating) {
- mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
- mSurfaceControl = new SurfaceControl(mSurfaceSession,
- "SurfaceView - " + viewRoot.getTitle().toString(),
- mSurfaceWidth, mSurfaceHeight, mFormat,
- mSurfaceFlags);
+ mLayout.format = mRequestedFormat;
+ mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_SCALED
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ ;
+ if (!creating && !sizeChanged) {
+ mLayout.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+ } else {
+ mLayout.privateFlags &=
+ ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
}
- boolean realSizeChanged = false;
+ if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
+ mLayout.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+ }
+ mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+
+ if (mWindow == null) {
+ Display display = getDisplay();
+ mWindow = new MyWindow(this);
+ mLayout.type = mWindowType;
+ mLayout.gravity = Gravity.START|Gravity.TOP;
+ mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
+ mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
+ mStableInsets);
+ }
+
+ boolean realSizeChanged;
+ boolean reportDrawNeeded;
+
+ int relayoutResult;
mSurfaceLock.lock();
try {
+ mUpdateWindowNeeded = false;
+ reportDrawNeeded = mReportDrawNeeded;
+ mReportDrawNeeded = false;
mDrawingStopped = !visible;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Cur surface: " + mSurface);
- SurfaceControl.openTransaction();
- try {
- mSurfaceControl.setLayer(mSubLayer);
- if (mViewVisibility) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
-
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- if (creating || !mRtHandlingPositionUpdates) {
- mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
- mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- mScreenRect.height() / (float) mSurfaceHeight);
- }
- if (sizeChanged) {
- mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
- }
- } finally {
- SurfaceControl.closeTransaction();
+ relayoutResult = mSession.relayout(
+ mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
+ visible ? VISIBLE : GONE,
+ WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
+ mWinFrame, mOverscanInsets, mContentInsets,
+ mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
+ mConfiguration, mNewSurface);
+ if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
+ reportDrawNeeded = true;
}
- if (sizeChanged || creating) {
- redrawNeeded = true;
- }
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "New surface: " + mNewSurface
+ + ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
mSurfaceFrame.top = 0;
if (mTranslator == null) {
- mSurfaceFrame.right = mSurfaceWidth;
- mSurfaceFrame.bottom = mSurfaceHeight;
+ mSurfaceFrame.right = mWinFrame.width();
+ mSurfaceFrame.bottom = mWinFrame.height();
} else {
float appInvertedScale = mTranslator.applicationInvertedScale;
- mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
- mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
}
final int surfaceWidth = mSurfaceFrame.right;
@@ -527,11 +576,12 @@
}
try {
- redrawNeeded |= visible && !mDrawFinished;
+ redrawNeeded |= creating | reportDrawNeeded;
SurfaceHolder.Callback callbacks[] = null;
- final boolean surfaceChanged = creating;
+ final boolean surfaceChanged = (relayoutResult
+ & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
if (mSurface.isValid()) {
@@ -558,10 +608,7 @@
}
}
- if (creating) {
- mSurface.copyFrom(mSurfaceControl);
- }
-
+ mSurface.transferFrom(mNewSurface);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
@@ -594,57 +641,53 @@
callbacks = getSurfaceCallbacks();
}
SurfaceCallbackHelper sch =
- new SurfaceCallbackHelper(this::onDrawFinished);
+ new SurfaceCallbackHelper(mSession, mWindow);
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
}
} finally {
mIsCreating = false;
- if (mSurfaceControl != null && !mSurfaceCreated) {
- mSurfaceControl.destroy();
- mSurfaceControl = null;
- }
+ mSession.performDeferredDestroy(mWindow);
}
- } catch (Exception ex) {
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
if (DEBUG) Log.v(
- TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
- + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
- + ", frame=" + mSurfaceFrame);
+ TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+ " w=" + mLayout.width + " h=" + mLayout.height +
+ ", frame=" + mSurfaceFrame);
} else {
// Calculate the window position in case RT loses the window
// and we need to fallback to a UI-thread driven position update
getLocationInWindow(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
- final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
- || getHeight() != mScreenRect.height();
if (positionChanged || layoutSizeChanged) { // Only the position has changed
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
- // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+ // For our size changed check, we keep mLayout.width and mLayout.height
// in view local space.
- mLocation[0] = getWidth();
- mLocation[1] = getHeight();
+ mLocation[0] = mLayout.width = getWidth();
+ mLocation[1] = mLayout.height = getHeight();
transformFromViewToWindowSpace(mLocation);
- mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+ mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop,
mLocation[0], mLocation[1]);
if (mTranslator != null) {
- mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ mTranslator.translateRectInAppWindowToScreen(mTmpRect);
}
if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
try {
- if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+ if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- setParentSpaceRectangle(mScreenRect, -1);
- } catch (Exception ex) {
+ mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom));
+ mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom, -1, mTmpRect);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
}
@@ -652,40 +695,18 @@
}
}
- private void onDrawFinished() {
- if (DEBUG) {
- Log.i(TAG, System.identityHashCode(this) + " "
- + "finishedDrawing");
- }
- mHandler.sendEmptyMessage(DRAW_FINISHED_MSG);
- }
-
- private void setParentSpaceRectangle(Rect position, long frameNumber) {
- ViewRootImpl viewRoot = getViewRootImpl();
-
- SurfaceControl.openTransaction();
- try {
- if (frameNumber > 0) {
- mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
- }
- mSurfaceControl.setPosition(position.left, position.top);
- mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- position.height() / (float) mSurfaceHeight);
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
private Rect mRTLastReportedPosition = new Rect();
/**
* Called by native by a Rendering Worker thread to update the window position
* @hide
*/
- public final void updateSurfacePosition_renderWorker(long frameNumber,
+ public final void updateWindowPosition_renderWorker(long frameNumber,
int left, int top, int right, int bottom) {
- if (mSurfaceControl == null) {
+ IWindowSession session = mSession;
+ MyWindow window = mWindow;
+ if (session == null || window == null) {
+ // Guess we got detached, that sucks
return;
}
// TODO: This is teensy bit racey in that a brand new SurfaceView moving on
@@ -705,29 +726,35 @@
}
try {
if (DEBUG) {
- Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+ Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
frameNumber, left, top, right, bottom));
}
- mRTLastReportedPosition.set(left, top, right, bottom);
- setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ // Just using mRTLastReportedPosition as a dummy rect here
+ session.repositionChild(window, left, top, right, bottom,
+ frameNumber,
+ mRTLastReportedPosition);
// Now overwrite mRTLastReportedPosition with our values
- } catch (Exception ex) {
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from repositionChild", ex);
}
}
/**
- * Called by native on RenderThread to notify that the view is no longer in the
+ * Called by native on RenderThread to notify that the window is no longer in the
* draw tree. UI thread is blocked at this point.
* @hide
*/
- public final void surfacePositionLost_uiRtSync(long frameNumber) {
+ public final void windowPositionLost_uiRtSync(long frameNumber) {
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
}
- if (mSurfaceControl == null) {
+ IWindowSession session = mSession;
+ MyWindow window = mWindow;
+ if (session == null || window == null) {
+ // We got detached prior to receiving this, abort
return;
}
if (mRtHandlingPositionUpdates) {
@@ -736,14 +763,19 @@
// safely access other member variables at this time.
// So do what the UI thread would have done if RT wasn't handling position
// updates.
- if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+ mTmpRect.set(mLayout.x, mLayout.y,
+ mLayout.x + mLayout.width,
+ mLayout.y + mLayout.height);
+
+ if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) {
try {
- if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+ if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- setParentSpaceRectangle(mScreenRect, frameNumber);
- } catch (Exception ex) {
+ mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom));
+ session.repositionChild(window, mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
}
@@ -760,6 +792,10 @@
return callbacks;
}
+ void handleGetNewSurface() {
+ updateWindow();
+ }
+
/**
* Check to see if the surface has fixed size dimensions or if the surface's
* dimensions are dimensions are dependent on its current layout.
@@ -771,8 +807,65 @@
return (mRequestedWidth != -1 || mRequestedHeight != -1);
}
- private boolean isAboveParent() {
- return mSubLayer >= 0;
+ private static class MyWindow extends BaseIWindow {
+ private final WeakReference<SurfaceView> mSurfaceView;
+
+ public MyWindow(SurfaceView surfaceView) {
+ mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+ }
+
+ @Override
+ public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
+ Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+ Configuration newConfig, Rect backDropRect, boolean forceLayout,
+ boolean alwaysConsumeNavBar, int displayId) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
+ + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+ surfaceView.mSurfaceLock.lock();
+ try {
+ if (reportDraw) {
+ surfaceView.mUpdateWindowNeeded = true;
+ surfaceView.mReportDrawNeeded = true;
+ surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+ } else if (surfaceView.mWinFrame.width() != frame.width()
+ || surfaceView.mWinFrame.height() != frame.height()
+ || forceLayout) {
+ surfaceView.mUpdateWindowNeeded = true;
+ surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+ }
+ } finally {
+ surfaceView.mSurfaceLock.unlock();
+ }
+ }
+ }
+
+ @Override
+ public void dispatchAppVisibility(boolean visible) {
+ // The point of SurfaceView is to let the app control the surface.
+ }
+
+ @Override
+ public void dispatchGetNewSurface() {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+ surfaceView.mHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+ Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
+ }
+
+ @Override
+ public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+ }
+
+ int mCurWidth = -1;
+ int mCurHeight = -1;
}
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@@ -820,14 +913,15 @@
@Override
public void setFormat(int format) {
+
// for backward compatibility reason, OPAQUE always
// means 565 for SurfaceView
if (format == PixelFormat.OPAQUE)
format = PixelFormat.RGB_565;
mRequestedFormat = format;
- if (mSurfaceControl != null) {
- updateSurface();
+ if (mWindow != null) {
+ updateWindow();
}
}
@@ -888,10 +982,10 @@
mSurfaceLock.lock();
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
- + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+ + mDrawingStopped + ", win=" + mWindow);
Canvas c = null;
- if (!mDrawingStopped && mSurfaceControl != null) {
+ if (!mDrawingStopped && mWindow != null) {
try {
if (hardware) {
c = mSurface.lockHardwareCanvas();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9863b0..20d960f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2632,14 +2632,6 @@
}
}
- private void onDrawFinished() {
- try {
- mWindowSession.finishDrawing(mWindow);
- } catch (RemoteException e) {
- // Have fun!
- }
- }
-
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
@@ -2690,7 +2682,7 @@
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index c840f26..ec3aac2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -48,12 +48,12 @@
List<NotificationChannel> channelsList = new ArrayList<NotificationChannel>();
channelsList.add(new NotificationChannel(
VIRTUAL_KEYBOARD,
- context.getString(R.string.notification_channel_virtual_keyboard),
+ R.string.notification_channel_virtual_keyboard,
NotificationManager.IMPORTANCE_LOW));
final NotificationChannel physicalKeyboardChannel = new NotificationChannel(
PHYSICAL_KEYBOARD,
- context.getString(R.string.notification_channel_physical_keyboard),
+ R.string.notification_channel_physical_keyboard,
NotificationManager.IMPORTANCE_DEFAULT);
physicalKeyboardChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -61,32 +61,32 @@
channelsList.add(new NotificationChannel(
SECURITY,
- context.getString(R.string.notification_channel_security),
+ R.string.notification_channel_security,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
CAR_MODE,
- context.getString(R.string.notification_channel_car_mode),
+ R.string.notification_channel_car_mode,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
DEVELOPER,
- context.getString(R.string.notification_channel_developer),
+ R.string.notification_channel_developer,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
UPDATES,
- context.getString(R.string.notification_channel_updates),
+ R.string.notification_channel_updates,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
NETWORK_STATUS,
- context.getString(R.string.notification_channel_network_status),
+ R.string.notification_channel_network_status,
NotificationManager.IMPORTANCE_LOW));
final NotificationChannel networkAlertsChannel = new NotificationChannel(
NETWORK_ALERTS,
- context.getString(R.string.notification_channel_network_alerts),
+ R.string.notification_channel_network_alerts,
NotificationManager.IMPORTANCE_HIGH);
networkAlertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -94,17 +94,17 @@
channelsList.add(new NotificationChannel(
VPN,
- context.getString(R.string.notification_channel_vpn),
+ R.string.notification_channel_vpn,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
DEVICE_ADMIN,
- context.getString(R.string.notification_channel_device_admin),
+ R.string.notification_channel_device_admin,
NotificationManager.IMPORTANCE_LOW));
final NotificationChannel alertsChannel = new NotificationChannel(
ALERTS,
- context.getString(R.string.notification_channel_alerts),
+ R.string.notification_channel_alerts,
NotificationManager.IMPORTANCE_DEFAULT);
alertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -112,12 +112,12 @@
channelsList.add(new NotificationChannel(
RETAIL_MODE,
- context.getString(R.string.notification_channel_retail_mode),
+ R.string.notification_channel_retail_mode,
NotificationManager.IMPORTANCE_LOW));
channelsList.add(new NotificationChannel(
USB,
- context.getString(R.string.notification_channel_usb),
+ R.string.notification_channel_usb,
NotificationManager.IMPORTANCE_MIN));
nm.createNotificationChannels(channelsList);
@@ -128,7 +128,7 @@
final NotificationManager nm = context.getSystemService(NotificationManager.class);
nm.createNotificationChannelsForPackage(pkg, Arrays.asList(new NotificationChannel(
ACCOUNT,
- context.getString(R.string.notification_channel_account),
+ R.string.notification_channel_account,
NotificationManager.IMPORTANCE_LOW)));
}
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
index 507b673..5b6a82c 100644
--- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -17,11 +17,14 @@
package com.android.internal.view;
import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
import android.view.Surface;
import android.view.SurfaceHolder;
public class SurfaceCallbackHelper {
- Runnable mRunnable;
+ IWindowSession mSession;
+ IWindow.Stub mWindow;
int mFinishDrawingCollected = 0;
int mFinishDrawingExpected = 0;
@@ -34,18 +37,26 @@
if (mFinishDrawingCollected < mFinishDrawingExpected) {
return;
}
- mRunnable.run();
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
}
}
};
- public SurfaceCallbackHelper(Runnable callbacksCollected) {
- mRunnable = callbacksCollected;
+ public SurfaceCallbackHelper(IWindowSession session,
+ IWindow.Stub window) {
+ mSession = session;
+ mWindow = window;
}
public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
if (callbacks == null || callbacks.length == 0) {
- mRunnable.run();
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
return;
}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c348cc5..a196540 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -232,6 +232,73 @@
return localBitmap->bitmap();
}
+void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info) {
+ SkASSERT(info);
+ SkASSERT(env);
+ SkASSERT(bitmap);
+ SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+ jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+ LocalScopedBitmap localBitmap(bitmapHandle);
+
+ const SkImageInfo& imageInfo = localBitmap->info();
+ info->width = imageInfo.width();
+ info->height = imageInfo.height();
+ info->stride = localBitmap->rowBytes();
+ info->flags = 0;
+ switch (imageInfo.colorType()) {
+ case kN32_SkColorType:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
+ break;
+ case kRGB_565_SkColorType:
+ info->format = ANDROID_BITMAP_FORMAT_RGB_565;
+ break;
+ case kARGB_4444_SkColorType:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
+ break;
+ case kAlpha_8_SkColorType:
+ info->format = ANDROID_BITMAP_FORMAT_A_8;
+ break;
+ default:
+ info->format = ANDROID_BITMAP_FORMAT_NONE;
+ break;
+ }
+}
+
+void* lockPixels(JNIEnv* env, jobject bitmap) {
+ SkASSERT(env);
+ SkASSERT(bitmap);
+ SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+ jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+
+ LocalScopedBitmap localBitmap(bitmapHandle);
+ if (!localBitmap->valid()) return nullptr;
+
+ SkPixelRef& pixelRef = localBitmap->bitmap();
+ pixelRef.lockPixels();
+ if (!pixelRef.pixels()) {
+ pixelRef.unlockPixels();
+ return nullptr;
+ }
+ pixelRef.ref();
+ return pixelRef.pixels();
+}
+
+bool unlockPixels(JNIEnv* env, jobject bitmap) {
+ SkASSERT(env);
+ SkASSERT(bitmap);
+ SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+ jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+
+ LocalScopedBitmap localBitmap(bitmapHandle);
+ if (!localBitmap->valid()) return false;
+
+ SkPixelRef& pixelRef = localBitmap->bitmap();
+ pixelRef.notifyPixelsChanged();
+ pixelRef.unlockPixels();
+ pixelRef.unref();
+ return true;
+}
+
} // namespace bitmap
} // namespace android
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index 387a128..a4bfc94 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -17,6 +17,7 @@
#define BITMAP_H_
#include <jni.h>
+#include <android/bitmap.h>
#include <SkBitmap.h>
#include <SkColorTable.h>
#include <SkImageInfo.h>
@@ -44,6 +45,13 @@
Bitmap& toBitmap(JNIEnv* env, jobject bitmap);
Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle);
+// NDK access
+void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info);
+// Returns a pointer to the pixels or nullptr if the bitmap is not valid
+void* lockPixels(JNIEnv* env, jobject bitmap);
+// Returns true if unlocked, false if the bitmap is no longer valid (destroyed)
+bool unlockPixels(JNIEnv* env, jobject bitmap);
+
/** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
sync with isPremultiplied
*/
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index edcbb3f..f221392 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -627,9 +627,9 @@
int register_android_view_RenderNode(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
- "updateSurfacePosition_renderWorker", "(JIIII)V");
+ "updateWindowPosition_renderWorker", "(JIIII)V");
gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
- "surfacePositionLost_uiRtSync", "(J)V");
+ "windowPositionLost_uiRtSync", "(J)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index be86f5c..a81901d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -693,6 +693,7 @@
return JNI_TRUE;
}
+
static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
jobject handleObject, jlong frameNumber) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -701,27 +702,6 @@
ctrl->deferTransactionUntil(handle, frameNumber);
}
-static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject,
- jobject surfaceObject, jlong frameNumber) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
-
- ctrl->deferTransactionUntil(barrier, frameNumber);
-}
-
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject,
- jobject newParentObject) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<IBinder> handle = ibinderForJavaObject(env, newParentObject);
-
- ctrl->reparentChildren(handle);
-}
-
-static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- ctrl->detachChildren();
-}
-
static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject,
jint scalingMode) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -844,12 +824,6 @@
(void*)nativeSetDisplayPowerMode },
{"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
(void*)nativeDeferTransactionUntil },
- {"nativeDeferTransactionUntilSurface", "(JJJ)V",
- (void*)nativeDeferTransactionUntilSurface },
- {"nativeReparentChildren", "(JLandroid/os/IBinder;)V",
- (void*)nativeReparentChildren } ,
- {"nativeSeverChildren", "(J)V",
- (void*)nativeSeverChildren } ,
{"nativeSetOverrideScalingMode", "(JI)V",
(void*)nativeSetOverrideScalingMode },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 508d897..dad6958 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -24,7 +24,6 @@
#include <utils/RefBase.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
namespace android {
@@ -46,13 +45,6 @@
return reinterpret_cast<jlong>(client);
}
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
- Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
- SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
- client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(client);
-}
-
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
@@ -63,12 +55,11 @@
client->dispose();
}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeCreateScoped", "(J)J",
- (void*)nativeCreateScoped },
{ "nativeDestroy", "(J)V",
(void*)nativeDestroy },
{ "nativeKill", "(J)V",
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 76f4a76..deacc24b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1534,6 +1534,14 @@
of Android higher than the number given here, the permission will not
be requested. -->
<attr name="maxSdkVersion" format="integer" />
+ <!-- Optional: the system must support this feature for the permission to be
+ requested. If it doesn't support the feature, it will be as if the manifest didn't
+ request it at all. -->
+ <attr name="requiredFeature" format="string" />
+ <!-- Optional: the system must NOT support this feature for the permission to be
+ requested. If it does support the feature, it will be as if the manifest didn't
+ request it at all. -->
+ <attr name="requiredNotFeature" format="string" />
</declare-styleable>
<!-- The <code>uses-configuration</code> tag specifies
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 01737e7..15764a9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2797,6 +2797,10 @@
<public name="fontProviderAuthority" />
<public name="fontProviderQuery" />
<public name="autoFillMode" />
+ <public name="primaryContentAlpha" />
+ <public name="secondaryContentAlpha" />
+ <public name="requiredFeature" />
+ <public name="requiredNotFeature" />
</public-group>
<public-group type="style" first-id="0x010302e0">
@@ -2806,9 +2810,6 @@
<public name="textAssist" />
</public-group>
- <public type="attr" name="primaryContentAlpha" />
- <public type="attr" name="secondaryContentAlpha" />
-
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index bf7a779a..d6b1cf1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -45,7 +45,6 @@
PURPOSE_DECRYPT,
PURPOSE_SIGN,
PURPOSE_VERIFY,
- PURPOSE_WRAP_KEY,
})
public @interface PurposeEnum {}
@@ -70,12 +69,6 @@
public static final int PURPOSE_VERIFY = 1 << 3;
/**
- * Purpose of key: wrapping key for secure key import.
- */
- public static final int PURPOSE_WRAP_KEY = 1 << 4;
-
-
- /**
* @hide
*/
public static abstract class Purpose {
@@ -91,8 +84,6 @@
return KeymasterDefs.KM_PURPOSE_SIGN;
case PURPOSE_VERIFY:
return KeymasterDefs.KM_PURPOSE_VERIFY;
- case PURPOSE_WRAP_KEY:
- return KeymasterDefs.KM_PURPOSE_WRAP_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -108,8 +99,6 @@
return PURPOSE_SIGN;
case KeymasterDefs.KM_PURPOSE_VERIFY:
return PURPOSE_VERIFY;
- case KeymasterDefs.KM_PURPOSE_WRAP_KEY:
- return PURPOSE_WRAP_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 35ff635..1b57e29 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -78,7 +78,7 @@
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
// updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
for (int i = layers.entries().size() - 1; i >= 0; i--) {
- RenderNode* layerNode = layers.entries()[i].renderNode;
+ RenderNode* layerNode = layers.entries()[i].renderNode.get();
// only schedule repaint if node still on layer - possible it may have been
// removed during a dropped frame, but layers may still remain scheduled so
// as not to lose info on what portion is damaged
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index 5b1a854..38f3596 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -19,6 +19,7 @@
#include "Rect.h"
#include "utils/Macros.h"
+#include <utils/StrongPointer.h>
#include <vector>
#include <unordered_map>
@@ -35,7 +36,7 @@
Entry(RenderNode* renderNode, const Rect& damage)
: renderNode(renderNode)
, damage(damage) {}
- RenderNode* renderNode;
+ sp<RenderNode> renderNode;
Rect damage;
};
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 117395b..39f11b8 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -197,12 +197,12 @@
if (needsLayer) {
canvas->saveLayer(bounds, &paint);
}
- canvas->drawDrawable(displayList->mDrawable.get());
+ displayList->draw(canvas);
if (needsLayer) {
canvas->restore();
}
} else {
- canvas->drawDrawable(displayList->mDrawable.get());
+ displayList->draw(canvas);
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index b4babcb..496f7ba 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,10 +27,6 @@
namespace uirenderer {
namespace skiapipeline {
-SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
- SkASSERT(projectionReceiveIndex == -1);
-}
-
void SkiaDisplayList::syncContents() {
for (auto& functor : mChildFunctors) {
functor.syncFunctor();
@@ -41,7 +37,7 @@
}
bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
- reset(SkRect::MakeEmpty());
+ reset();
node->attachAvailableList(this);
return true;
}
@@ -102,10 +98,10 @@
return isDirty;
}
-void SkiaDisplayList::reset(SkRect bounds) {
+void SkiaDisplayList::reset() {
mProjectionReceiver = nullptr;
- mDrawable->reset(bounds);
+ mDisplayList.reset();
mMutableImages.clear();
mVectorDrawables.clear();
@@ -119,7 +115,7 @@
void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
DumpOpsCanvas canvas(output, level, *this);
- mDrawable->draw(&canvas, nullptr);
+ mDisplayList.draw(&canvas);
}
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 439b999..6ee5922 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,7 +22,7 @@
#include <deque>
#include <SkLiteDL.h>
-#include <SkPictureRecorder.h>
+#include <SkLiteRecorder.h>
namespace android {
namespace uirenderer {
@@ -39,22 +39,22 @@
*/
class SkiaDisplayList : public DisplayList {
public:
- SkiaDisplayList(SkRect bounds);
+ SkiaDisplayList() { SkASSERT(projectionReceiveIndex == -1); }
virtual ~SkiaDisplayList() {
/* Given that we are using a LinearStdAllocator to store some of the
* SkDrawable contents we must ensure that any other object that is
* holding a reference to those drawables is destroyed prior to their
* deletion.
*/
- mDrawable.reset();
+ mDisplayList.reset();
}
/**
* This resets the DisplayList so that it behaves as if the object were newly
- * constructed with the provided bounds. The reuse avoids any overhead
- * associated with destroying the SkLiteDL as well as the deques and vectors.
+ * constructed. The reuse avoids any overhead associated with destroying
+ * the SkLiteDL as well as the deques and vectors.
*/
- void reset(SkRect bounds);
+ void reset();
/**
* Use the linear allocator to create any SkDrawables needed by the display
@@ -72,7 +72,7 @@
/**
* Returns true if the DisplayList does not have any recorded content
*/
- bool isEmpty() const override { return mDrawable->empty(); }
+ bool isEmpty() const override { return mDisplayList.empty(); }
/**
* Returns true if this list directly contains a GLFunctor drawing command.
@@ -126,18 +126,24 @@
*/
inline bool containsProjectionReceiver() const { return mProjectionReceiver; }
+ void attachRecorder(SkLiteRecorder* recorder, const SkIRect& bounds) {
+ recorder->reset(&mDisplayList, bounds);
+ }
+
+ void draw(SkCanvas* canvas) { mDisplayList.draw(canvas); }
+
void output(std::ostream& output, uint32_t level) override;
/**
* We use std::deque here because (1) we need to iterate through these
- * elements and (2) mDrawable holds pointers to the elements, so they cannot
- * relocate.
+ * elements and (2) mDisplayList holds pointers to the elements, so they
+ * cannot relocate.
*/
std::deque<RenderNodeDrawable> mChildNodes;
std::deque<GLFunctorDrawable> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<VectorDrawableRoot*> mVectorDrawables;
- sk_sp<SkLiteDL> mDrawable;
+ SkLiteDL mDisplayList;
//mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
//receiver. It is set at record time and used at both prepare and draw tree traversals to
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 430d6be..11dc1f4 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -80,7 +80,7 @@
void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
// Render all layers that need to be updated, in order.
for (size_t i = 0; i < layers.entries().size(); i++) {
- RenderNode* layerNode = layers.entries()[i].renderNode;
+ RenderNode* layerNode = layers.entries()[i].renderNode.get();
// only schedule repaint if node still on layer - possible it may have been
// removed during a dropped frame, but layers may still remain scheduled so
// as not to lose info on what portion is damaged
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index dbe0296..559d268 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -39,14 +39,11 @@
if (renderNode) {
mDisplayList = renderNode->detachAvailableList();
}
- SkRect bounds = SkRect::MakeWH(width, height);
- if (mDisplayList) {
- mDisplayList->reset(bounds);
- } else {
- mDisplayList.reset(new SkiaDisplayList(bounds));
+ if (!mDisplayList) {
+ mDisplayList.reset(new SkiaDisplayList());
}
- mRecorder.reset(mDisplayList->mDrawable.get());
+ mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
SkiaCanvas::reset(&mRecorder);
}
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 4db1cb9..91c7514 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -48,11 +48,11 @@
EXPECT_EQ(3u, queue.entries().size());
- EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode.get());
EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
- EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
+ EXPECT_EQ(b.get(), queue.entries()[1].renderNode.get());
EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
- EXPECT_EQ(c.get(), queue.entries()[2].renderNode);
+ EXPECT_EQ(c.get(), queue.entries()[2].renderNode.get());
EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out
}
@@ -65,7 +65,7 @@
EXPECT_EQ(1u, queue.entries().size());
- EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+ EXPECT_EQ(a.get(), queue.entries()[0].renderNode.get());
EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
}
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index f21b3f7..cbea501 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -44,9 +44,9 @@
canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
- auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
+ SkLiteDL skLiteDL;
SkLiteRecorder canvas;
- canvas.reset(skLiteDL.get());
+ canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
canvas.translate(100, 100);
RenderNodeDrawable drawable(rootNode.get(), &canvas);
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 2925243..eda4a9d 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -331,7 +331,7 @@
// damage rect.
EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
- EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
+ EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode.get());
EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
canvasContext->destroy();
}
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index be460bf..dd8f4b4 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -30,16 +30,13 @@
using namespace android::uirenderer::skiapipeline;
TEST(SkiaDisplayList, create) {
- SkRect bounds = SkRect::MakeWH(200, 200);
- SkiaDisplayList skiaDL(bounds);
+ SkiaDisplayList skiaDL;
ASSERT_TRUE(skiaDL.isEmpty());
ASSERT_FALSE(skiaDL.mProjectionReceiver);
- ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
}
TEST(SkiaDisplayList, reset) {
- SkRect bounds = SkRect::MakeWH(200, 200);
- SkiaDisplayList skiaDL(bounds);
+ SkiaDisplayList skiaDL;
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
@@ -47,10 +44,9 @@
skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
skiaDL.mMutableImages.push_back(nullptr);
skiaDL.mVectorDrawables.push_back(nullptr);
- skiaDL.mDrawable->drawAnnotation(bounds, "testAnnotation", nullptr);
+ skiaDL.mDisplayList.drawAnnotation(SkRect::MakeWH(200, 200), "testAnnotation", nullptr);
skiaDL.mProjectionReceiver = &drawable;
- ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
ASSERT_FALSE(skiaDL.mChildNodes.empty());
ASSERT_FALSE(skiaDL.mChildFunctors.empty());
ASSERT_FALSE(skiaDL.mMutableImages.empty());
@@ -58,10 +54,8 @@
ASSERT_FALSE(skiaDL.isEmpty());
ASSERT_TRUE(skiaDL.mProjectionReceiver);
- bounds = SkRect::MakeWH(100, 100);
- skiaDL.reset(bounds);
+ skiaDL.reset();
- ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
ASSERT_TRUE(skiaDL.mChildNodes.empty());
ASSERT_TRUE(skiaDL.mChildFunctors.empty());
ASSERT_TRUE(skiaDL.mMutableImages.empty());
@@ -79,7 +73,7 @@
ASSERT_EQ(availableList.get(), nullptr);
// attach a displayList for reuse
- SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+ SkiaDisplayList skiaDL;
ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
// detach the list that you just attempted to reuse
@@ -93,13 +87,13 @@
}
TEST(SkiaDisplayList, syncContexts) {
- SkRect bounds = SkRect::MakeWH(200, 200);
- SkiaDisplayList skiaDL(bounds);
+ SkiaDisplayList skiaDL;
SkCanvas dummyCanvas;
TestUtils::MockFunctor functor;
skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+ SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
vectorDrawable.mutateStagingProperties()->setBounds(bounds);
skiaDL.mVectorDrawables.push_back(&vectorDrawable);
@@ -127,7 +121,7 @@
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
- SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+ SkiaDisplayList skiaDL;
// prepare with a clean VD
VectorDrawableRoot cleanVD(new VectorDrawable::Group());
@@ -170,8 +164,7 @@
}
TEST(SkiaDisplayList, updateChildren) {
- SkRect bounds = SkRect::MakeWH(200, 200);
- SkiaDisplayList skiaDL(bounds);
+ SkiaDisplayList skiaDL;
sp<RenderNode> renderNode = new RenderNode();
SkCanvas dummyCanvas;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1ca9658..d264127 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3188,9 +3188,55 @@
/**
* Returns Analytics/Metrics data about the current content being
*
- * @return a Bundle containint the set of attributes and values available
+ * @return a Bundle containing the set of attributes and values available
* for the media being handled by this instance of MediaCodec
*
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th>Key</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@code "codec"}</td>
+ * <td>String</td>
+ * <td>Identifies the particular codec in use</td>
+ * </tr><tr>
+ * <td>{@code "mime"}</td>
+ * <td>String</td>
+ * <td>Mime type of the media being encoded/decoded</td>
+ * </tr><tr>
+ * <td>{@code "mode"}</td>
+ * <td>String</td>
+ * <td>"Audio" or "Video"</td>
+ * </tr><tr>
+ * <td>{@code "secure"}</td>
+ * <td>Integer</td>
+ * <td>Indicates whether the code is operating on secure content and
+ * may also use capabilities in android.media.MediaCrypto</td>
+ * </tr><tr>
+ * <td>{@code "height"}</td>
+ * <td>Integer</td>
+ * <td>Height (pixels); valid only when mode=video</td>
+ * </tr><tr>
+ * <td>{@code "width"}</td>
+ * <td>Integer</td>
+ * <td>Width (pixels); valid only when mode=video</td>
+ * </tr><tr>
+ * <td>{@code "rotation"}</td>
+ * <td>Integer</td>
+ * <td>rotation (degrees) to orient the video onto the target surface;
+ * valid only when mode=video. Note there may be additional
+ * rotations applied when the surface is mapped to the screen.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Additional fields specific to individual codecs will also appear in
+ * the return value.
*/
public native Bundle getMetrics();
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 01ae36f..b9e409d 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -656,6 +656,33 @@
* @return the set of keys and values available for the media being
* handled by this instance of MediaExtractor
*
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th>Key</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@code "fmt"}</td>
+ * <td>String</td>
+ * <td>The container format (which determines the handler)</td>
+ * </tr><tr>
+ * <td>{@code "mime"}</td>
+ * <td>String</td>
+ * <td>Mime type of the container.</td>
+ * </tr><tr>
+ * <td>{@code "ntrk"}</td>
+ * <td>Integer</td>
+ * <td>Number of tracks in the container</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Additional fields specific to individual codecs will also appear in
+ * the return value.
*/
public native Bundle getMetrics();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fb37f9f..b85c911 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1498,6 +1498,64 @@
*
* @return the a map of attributes and values available for this video
* player or null if no metrics are available.
+ *
+ * <table style="width: 0%">
+ * <thead>
+ * <tr>
+ * <th>Key</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@code "video/codec"}</td>
+ * <td>String</td>
+ * <td>Identifies the video codec in use</td>
+ * </tr><tr>
+ * <td>{@code "video/mime"}</td>
+ * <td>String</td>
+ * <td>Mime type of the video being encoded/decoded</td>
+ * </tr><tr>
+ * <td>{@code "audio/codec"}</td>
+ * <td>String</td>
+ * <td>Identifies the audio codec in use</td>
+ * </tr><tr>
+ * <td>{@code "audio/mime"}</td>
+ * <td>String</td>
+ * <td>Mime type of the audio being encoded/decoded</td>
+ * </tr><tr>
+ * <td>{@code "ht"}</td>
+ * <td>Integer</td>
+ * <td>Height (pixels); valid only when mode=video</td>
+ * </tr><tr>
+ * <td>{@code "wid"}</td>
+ * <td>Integer</td>
+ * <td>Width (pixels); valid only when mode=video</td>
+ * </tr><tr>
+ * <td>{@code "frame"}</td>
+ * <td>Integer</td>
+ * <td>Number of decoded video frames sent to the display</td>
+ * </tr><tr>
+ * <td>{@code "dropped"}</td>
+ * <td>Integer</td>
+ * <td>Number of decoded video frames that were not sent to display.
+ * These frames were dropped by the player.</td>
+ * </tr><tr>
+ * <td>{@code "durationMs"}</td>
+ * <td>Integer</td>
+ * <td>The length of the media being played (in ms), e.g. "This video lasts for 30000 milliseconds". </td>
+ * </tr><tr>
+ * <td>{@code "playingMs"}</td>
+ * <td>Integer</td>
+ * <td>The time the media has been played (in ms). If you watch a
+ * 30 second twice through, this will report 60000 ms.</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * Additional fields specific to individual codecs will also appear in
+ * the return value.
*/
public native Bundle getMetrics();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index e5af357..aee9d38e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -776,8 +776,8 @@
mSurface = null;
mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
@Override
- protected void updateSurface() {
- super.updateSurface();
+ protected void updateWindow() {
+ super.updateWindow();
relayoutSessionOverlayView();
}};
// The surface view's content should be treated as secure all the time.
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 6d2de98..bf5cabb 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -15,11 +15,7 @@
*/
#include <android/bitmap.h>
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#include <GraphicsJNI.h>
-#pragma GCC diagnostic pop
+#include <Bitmap.h>
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info) {
@@ -27,32 +23,8 @@
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkBitmap bm;
- GraphicsJNI::getSkBitmap(env, jbitmap, &bm);
-
if (info) {
- info->width = bm.width();
- info->height = bm.height();
- info->stride = bm.rowBytes();
- info->flags = 0;
-
- switch (bm.colorType()) {
- case kN32_SkColorType:
- info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
- break;
- case kRGB_565_SkColorType:
- info->format = ANDROID_BITMAP_FORMAT_RGB_565;
- break;
- case kARGB_4444_SkColorType:
- info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
- break;
- case kAlpha_8_SkColorType:
- info->format = ANDROID_BITMAP_FORMAT_A_8;
- break;
- default:
- info->format = ANDROID_BITMAP_FORMAT_NONE;
- break;
- }
+ android::bitmap::imageInfo(env, jbitmap, info);
}
return ANDROID_BITMAP_RESULT_SUCCESS;
}
@@ -62,19 +34,11 @@
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
- if (!pixelRef) {
+ void* addr = android::bitmap::lockPixels(env, jbitmap);
+ if (!addr) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
- pixelRef->lockPixels();
- void* addr = pixelRef->pixels();
- if (NULL == addr) {
- pixelRef->unlockPixels();
- pixelRef->unref();
- return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
- }
-
if (addrPtr) {
*addrPtr = addr;
}
@@ -86,26 +50,10 @@
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
- if (!pixelRef) {
+ bool unlocked = android::bitmap::unlockPixels(env, jbitmap);
+ if (!unlocked) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
-
- // notifyPixelsChanged() needs be called to apply writes to GL-backed
- // bitmaps. Note that this will slow down read-only accesses to the
- // bitmaps, but the NDK methods are primarily intended to be used for
- // writes.
- pixelRef->notifyPixelsChanged();
-
- pixelRef->unlockPixels();
- // Awkward in that we need to double-unref as the call to get the SkPixelRef
- // did a ref(), so we need to unref() for the local ref and for the previous
- // AndroidBitmap_lockPixels(). However this keeps GraphicsJNI a bit safer
- // if others start using it without knowing about android::Bitmap's "fun"
- // ref counting mechanism(s).
- pixelRef->unref();
- pixelRef->unref();
-
return ANDROID_BITMAP_RESULT_SUCCESS;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 7f7249f0..b06b032 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -28,6 +28,7 @@
import android.util.Log;
import com.android.settingslib.R;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +36,13 @@
public class AppUtils {
private static final String TAG = "AppUtils";
+ /**
+ * This should normally only be set in robolectric tests, to avoid getting a method not found
+ * exception when calling the isInstantApp method of the ApplicationInfo class, because
+ * robolectric does not yet have an implementation of it.
+ */
+ private static InstantAppDataProvider sInstantAppDataProvider = null;
+
public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
IUsbManager usbManager, PackageManager pm, Context context) {
String packageName = appEntry.info.packageName;
@@ -74,7 +82,11 @@
* Returns a boolean indicating whether the given package should be considered an instant app
*/
public static boolean isInstant(ApplicationInfo info) {
- if (info.isInstantApp()) {
+ if (sInstantAppDataProvider != null) {
+ if (sInstantAppDataProvider.isInstantApp(info)) {
+ return true;
+ }
+ } else if (info.isInstantApp()) {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java b/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java
new file mode 100644
index 0000000..8b15715
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.settingslib.applications.instantapps;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * This helps deal with the fact that robolectric does not yet have an implementation of the
+ * isInstantApp method of ApplicationInfo, so we get a method not found exception when running tests
+ * if we try to call it directly.
+ */
+public interface InstantAppDataProvider {
+ public boolean isInstantApp(ApplicationInfo info);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 3df7e66..50972c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -223,7 +223,8 @@
}
final Context context = getContext();
- int level = WifiManager.calculateSignalLevel(mAccessPoint.getRssi(), 5 /* levels */);
+ int level = WifiManager.calculateSignalLevel(
+ mAccessPoint.getRssi(), WifiManager.RSSI_LEVELS);
int wifiBadge = mAccessPoint.getBadge();
if (level != mLevel || wifiBadge != mWifiBadge) {
mLevel = level;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index ddee89e..674ed5a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -19,7 +19,7 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.ProvidesInterface;
-@ProvidesInterface(action = NavGesture.ACTION, version = NavBarButtonProvider.VERSION)
+@ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION)
public interface NavGesture extends Plugin {
public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_GESTURE";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index f7bfc1e..62aa466 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -23,16 +23,18 @@
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
+import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.Checkable;
import android.widget.ImageView;
+import android.widget.Switch;
import android.widget.TextView;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.BatteryInfo;
import com.android.settingslib.graph.BatteryMeterDrawableBase;
@@ -43,9 +45,8 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.BatteryController;
-import java.text.NumberFormat;
-
-public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback {
+public class BatterySaverTile extends QSTile<QSTile.BooleanState> implements
+ BatteryController.BatteryStateChangeCallback {
private final BatteryController mBatteryController;
private final BatteryDetail mBatteryDetail = new BatteryDetail();
@@ -56,19 +57,14 @@
private boolean mDetailShown;
private boolean mPluggedIn;
- public BatteryTile(Host host) {
+ public BatterySaverTile(Host host) {
super(host);
mBatteryController = Dependency.get(BatteryController.class);
}
@Override
- public State newTileState() {
- return new QSTile.State();
- }
-
- @Override
- public DetailAdapter getDetailAdapter() {
- return mBatteryDetail;
+ public BooleanState newTileState() {
+ return new BooleanState();
}
@Override
@@ -109,22 +105,15 @@
}
@Override
- protected void handleUpdateState(State state, Object arg) {
- int level = (arg != null) ? (Integer) arg : mLevel;
- String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
-
+ protected void handleUpdateState(BooleanState state, Object arg) {
state.state = mCharging ? Tile.STATE_UNAVAILABLE
: mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(R.drawable.ic_qs_battery_saver);
state.label = mContext.getString(R.string.battery_detail_switch_title);
- state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery,
- percentage) + "," +
- (mPowerSave ? mContext.getString(R.string.battery_saver_notification_title)
- : mCharging ? mContext.getString(R.string.expanded_header_battery_charging)
- : "")
- + "," + mContext.getString(R.string.accessibility_battery_details);
+ state.contentDescription = state.label;
+ state.value = mPowerSave;
state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
- = Button.class.getName();
+ = Switch.class.getName();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 9703235..807d902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -72,11 +72,6 @@
private StatusBarNotification mStatusBarNotification;
private NotificationChannel mNotificationChannel;
- private ImageView mAutoButton;
- private TextView mImportanceSummary;
- private TextView mImportanceTitle;
- private boolean mAuto;
-
private TextView mNumChannelsView;
private View mChannelDisabledView;
private Switch mChannelEnabledSwitch;
@@ -105,8 +100,10 @@
int appUid = -1;
String appName = pkg;
Drawable pkgicon = null;
+ CharSequence channelNameText = "";
+ ApplicationInfo info = null;
try {
- final ApplicationInfo info = pm.getApplicationInfo(pkg,
+ info = pm.getApplicationInfo(pkg,
PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -115,6 +112,7 @@
appUid = info.uid;
appName = String.valueOf(pm.getApplicationLabel(info));
pkgicon = pm.getApplicationIcon(info);
+
}
} catch (PackageManager.NameNotFoundException e) {
// app is gone, just show package name and generic icon
@@ -135,11 +133,15 @@
R.plurals.notification_num_channels_desc, numChannels), numChannels));
// If this is the placeholder channel, don't use our channel-specific text.
- CharSequence channelNameText;
if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
channelNameText = mContext.getString(R.string.notification_header_default_channel);
} else {
- channelNameText = channel.getName();
+ if (info != null && channel.getNameResId() != 0) {
+ channelNameText = pm.getText(pkg, channel.getNameResId(), info);
+ }
+ if (channel.getName() != null) {
+ channelNameText = channel.getName();
+ }
}
((TextView) findViewById(R.id.pkgname)).setText(appName);
((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
@@ -171,8 +173,8 @@
boolean nonBlockable = false;
try {
- final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
- nonBlockable = Utils.isSystemPackage(getResources(), pm, info);
+ final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
+ nonBlockable = Utils.isSystemPackage(getResources(), pm, pkgInfo);
} catch (PackageManager.NameNotFoundException e) {
// unlikely.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index a1022c4..de00237 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -37,7 +37,7 @@
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.BatteryTile;
+import com.android.systemui.qs.tiles.BatterySaverTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
@@ -273,7 +273,7 @@
else if (tileSpec.equals("cast")) return new CastTile(this);
else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
else if (tileSpec.equals("user")) return new UserTile(this);
- else if (tileSpec.equals("battery")) return new BatteryTile(this);
+ else if (tileSpec.equals("battery")) return new BatterySaverTile(this);
else if (tileSpec.equals("saver")) return new DataSaverTile(this);
else if (tileSpec.equals("night")) return new NightDisplayTile(this);
else if (tileSpec.equals("nfc")) return new NfcTile(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 821e635..a87b50a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -116,19 +116,20 @@
}
private void notifyKeyguardChanged() {
- mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+ // Copy the list to allow removal during callback.
+ new ArrayList<Callback>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
}
public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
mKeyguardFadingAway = true;
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
- mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+ notifyKeyguardChanged();
}
public void notifyKeyguardDoneFading() {
mKeyguardFadingAway = false;
- mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+ notifyKeyguardChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 51d931e..bc3eec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -39,6 +39,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
@@ -78,6 +79,7 @@
private static final int EMERGENCY_FIRST_CONTROLLER = 100;
private static final int EMERGENCY_VOICE_CONTROLLER = 200;
private static final int EMERGENCY_NO_SUB = 300;
+ private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
private final Context mContext;
private final TelephonyManager mPhone;
@@ -99,8 +101,7 @@
final EthernetSignalController mEthernetSignalController;
@VisibleForTesting
- final Map<Integer, MobileSignalController> mMobileSignalControllers =
- new HashMap<Integer, MobileSignalController>();
+ final SparseArray<MobileSignalController> mMobileSignalControllers = new SparseArray<>();
// When no SIMs are around at setup, and one is added later, it seems to default to the first
// SIM for most actions. This may be null if there aren't any SIMs around.
private MobileSignalController mDefaultSignalController;
@@ -232,7 +233,8 @@
}
private void registerListeners() {
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.registerListener();
}
if (mSubscriptionListener == null) {
@@ -261,7 +263,8 @@
private void unregisterListeners() {
mListening = false;
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.unregisterListener();
}
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
@@ -305,7 +308,7 @@
if (DEBUG) Log.e(TAG, "No data sim selected");
return mDefaultSignalController;
}
- if (mMobileSignalControllers.containsKey(dataSubId)) {
+ if (mMobileSignalControllers.indexOfKey(dataSubId) >= 0) {
return mMobileSignalControllers.get(dataSubId);
}
if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
@@ -326,8 +329,8 @@
}
int voiceSubId = mSubDefaults.getDefaultVoiceSubId();
if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
- for (MobileSignalController mobileSignalController :
- mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
if (!mobileSignalController.getState().isEmergency) {
mEmergencySource = EMERGENCY_FIRST_CONTROLLER
+ mobileSignalController.mSubscriptionInfo.getSubscriptionId();
@@ -336,11 +339,20 @@
}
}
}
- if (mMobileSignalControllers.containsKey(voiceSubId)) {
+ if (mMobileSignalControllers.indexOfKey(voiceSubId) >= 0) {
mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId;
if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId);
return mMobileSignalControllers.get(voiceSubId).getState().isEmergency;
}
+ // If we have the wrong subId but there is only one sim anyway, assume it should be the
+ // default.
+ if (mMobileSignalControllers.size() == 1) {
+ mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER
+ + mMobileSignalControllers.keyAt(0);
+ if (DEBUG) Log.d(TAG, "Getting assumed emergency from "
+ + mMobileSignalControllers.keyAt(0));
+ return mMobileSignalControllers.valueAt(0).getState().isEmergency;
+ }
if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
mEmergencySource = EMERGENCY_NO_SUB + voiceSubId;
// Something is wrong, better assume we can't make calls...
@@ -363,7 +375,8 @@
cb.setNoSims(mHasNoSims);
mWifiSignalController.notifyListeners(cb);
mEthernetSignalController.notifyListeners(cb);
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners(cb);
}
mCallbackHandler.setListening(cb, true);
@@ -416,7 +429,8 @@
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
// Notify every MobileSignalController so they can know whether they are the
// data sim or not.
- for (MobileSignalController controller : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.handleBroadcast(intent);
}
} else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
@@ -433,7 +447,7 @@
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- if (mMobileSignalControllers.containsKey(subId)) {
+ if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
mMobileSignalControllers.get(subId).handleBroadcast(intent);
} else {
// Can't find this subscription... We must be out of date.
@@ -458,8 +472,9 @@
@VisibleForTesting
void handleConfigurationChanged() {
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
- mobileSignalController.setConfiguration(mConfig);
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
+ controller.setConfiguration(mConfig);
}
refreshLocale();
}
@@ -511,15 +526,20 @@
});
mCurrentSubscriptions = subscriptions;
- HashMap<Integer, MobileSignalController> cachedControllers =
- new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
+ SparseArray<MobileSignalController> cachedControllers =
+ new SparseArray<MobileSignalController>();
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ cachedControllers.put(mMobileSignalControllers.keyAt(i),
+ mMobileSignalControllers.valueAt(i));
+ }
mMobileSignalControllers.clear();
final int num = subscriptions.size();
for (int i = 0; i < num; i++) {
int subId = subscriptions.get(i).getSubscriptionId();
// If we have a copy of this controller already reuse it, otherwise make a new one.
- if (cachedControllers.containsKey(subId)) {
- mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
+ if (cachedControllers.indexOfKey(subId) >= 0) {
+ mMobileSignalControllers.put(subId, cachedControllers.get(subId));
+ cachedControllers.remove(subId);
} else {
MobileSignalController controller = new MobileSignalController(mContext, mConfig,
mHasMobileDataFeature, mPhone, mCallbackHandler,
@@ -535,7 +555,8 @@
}
}
if (mListening) {
- for (Integer key : cachedControllers.keySet()) {
+ for (int i = 0; i < cachedControllers.size(); i++) {
+ int key = cachedControllers.keyAt(i);
if (cachedControllers.get(key) == mDefaultSignalController) {
mDefaultSignalController = null;
}
@@ -557,7 +578,8 @@
private void handleSetUserSetupComplete(boolean userSetup) {
mUserSetup = userSetup;
- for (MobileSignalController controller : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.setUserSetupComplete(mUserSetup);
}
}
@@ -568,7 +590,7 @@
return false;
}
for (SubscriptionInfo info : allSubscriptions) {
- if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
+ if (mMobileSignalControllers.indexOfKey(info.getSubscriptionId()) < 0) {
return false;
}
}
@@ -580,7 +602,8 @@
Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
if (airplaneMode != mAirplaneMode || force) {
mAirplaneMode = airplaneMode;
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.setAirplaneMode(mAirplaneMode);
}
notifyListeners();
@@ -601,7 +624,8 @@
*/
private void notifyAllListeners() {
notifyListeners();
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners();
}
mWifiSignalController.notifyListeners();
@@ -650,7 +674,8 @@
*/
private void pushConnectivityToSignals() {
// We want to update all the icons, all at once, for any condition change
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
@@ -682,7 +707,8 @@
pw.print(" mEmergencySource=");
pw.println(emergencyToString(mEmergencySource));
- for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.dump(pw);
}
mWifiSignalController.dump(pw);
@@ -694,6 +720,9 @@
private static final String emergencyToString(int emergencySource) {
if (emergencySource > EMERGENCY_NO_SUB) {
+ return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER)
+ + ")";
+ } else if (emergencySource > EMERGENCY_NO_SUB) {
return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")";
} else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) {
return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")";
@@ -724,7 +753,8 @@
// Update what MobileSignalControllers, because they may change
// to set the number of sim slots.
updateMobileControllers();
- for (MobileSignalController controller : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.resetLastState();
}
mWifiSignalController.resetLastState();
@@ -747,7 +777,8 @@
connected.set(mWifiSignalController.mTransportType);
}
mWifiSignalController.updateConnectivity(connected, connected);
- for (MobileSignalController controller : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
if (mDemoInetCondition) {
connected.set(controller.mTransportType);
}
@@ -820,8 +851,7 @@
mCallbackHandler.setSubs(subs);
}
// Hack to index linearly for easy use.
- MobileSignalController controller = mMobileSignalControllers
- .values().toArray(new MobileSignalController[0])[slot];
+ MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
controller.getState().dataSim = datatype != null;
controller.getState().isDefault = datatype != null;
controller.getState().dataConnected = datatype != null;
@@ -875,7 +905,8 @@
String carrierNetworkChange = args.getString("carriernetworkchange");
if (carrierNetworkChange != null) {
boolean show = carrierNetworkChange.equals("show");
- for (MobileSignalController controller : mMobileSignalControllers.values()) {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.setCarrierNetworkChangeMode(show);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 15ad0ce..5911766 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -36,19 +36,19 @@
nm.createNotificationChannels(Arrays.asList(
new NotificationChannel(
ALERTS,
- context.getString(R.string.notification_channel_alerts),
+ R.string.notification_channel_alerts,
NotificationManager.IMPORTANCE_HIGH),
new NotificationChannel(
SCREENSHOTS,
- context.getString(R.string.notification_channel_screenshot),
+ R.string.notification_channel_screenshot,
NotificationManager.IMPORTANCE_LOW),
new NotificationChannel(
GENERAL,
- context.getString(R.string.notification_channel_general),
+ R.string.notification_channel_general,
NotificationManager.IMPORTANCE_MIN),
new NotificationChannel(
STORAGE,
- context.getString(R.string.notification_channel_storage),
+ R.string.notification_channel_storage,
NotificationManager.IMPORTANCE_LOW)
));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 0491fc4..9a3fabb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -19,6 +19,8 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -98,6 +100,9 @@
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
+ eq(R.string.notification_menu_accessibility), anyObject())).thenReturn(
+ getContext().getString(R.string.notification_menu_accessibility));
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(1);
@@ -178,6 +183,19 @@
@Test
@UiThreadTest
+ public void testBindNotification_SetsTextChannelName_resId() throws Exception {
+ NotificationChannel notificationChannelResId = new NotificationChannel(
+ TEST_CHANNEL, R.string.notification_menu_accessibility,
+ NotificationManager.IMPORTANCE_LOW);
+ mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ mMockStatusBarNotification, notificationChannelResId, null, null, null);
+ final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+ assertEquals(getContext().getString(R.string.notification_menu_accessibility),
+ textView.getText());
+ }
+
+ @Test
+ @UiThreadTest
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index ba20999..e47f750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -12,6 +12,7 @@
import com.android.settingslib.net.DataUsageController;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -106,6 +107,7 @@
TelephonyIcons.QS_DATA_4G);
}
+ @Ignore("Flaky")
@Test
public void testDataDisabledIcon() {
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 2c0f9c9..1555856 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -94,6 +94,14 @@
}
@Test
+ public void testNoEmergencyOnlyWrongSubscription() {
+ setupDefaultSignal();
+ setDefaultSubId(42);
+ mNetworkController.recalculateEmergency();
+ verifyEmergencyOnly(false);
+ }
+
+ @Test
public void testNoEmengencyNoSubscriptions() {
setupDefaultSignal();
setSubscriptions();
@@ -286,12 +294,12 @@
for (int i = 0; i < testSubscriptions.length; i++) {
if (i == indexToSkipController) {
// Make sure a controller was created despite us not adding one.
- assertTrue(mNetworkController.mMobileSignalControllers.containsKey(
- testSubscriptions[i]));
+ assertTrue(mNetworkController.mMobileSignalControllers.indexOfKey(
+ testSubscriptions[i]) >= 0);
} else if (i == indexToSkipSubscription) {
// Make sure the controller that did exist was removed
- assertFalse(mNetworkController.mMobileSignalControllers.containsKey(
- testSubscriptions[i]));
+ assertFalse(mNetworkController.mMobileSignalControllers.indexOfKey(
+ testSubscriptions[i]) >= 0);
} else {
// If a MobileSignalController is around it needs to not be unregistered.
Mockito.verify(mobileSignalControllers[i], Mockito.never())
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4bacb58..2b219e6 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2157,7 +2157,7 @@
// ACTION: Settings -> Select summary tab.
// CATEGORY: SETTINGS
- ACTION_SELECT_SUMMARY=476;
+ ACTION_SELECT_SUMMARY = 476;
// ACTION: Settings -> Select support tab.
// CATEGORY: SETTINGS
@@ -2208,7 +2208,7 @@
NIGHT_DISPLAY_SETTINGS = 488;
// ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager
- // SUBTYPE: false is off, true is on
+ // SUBTYPE: false is off, true is on
ACTION_TOGGLE_STORAGE_MANAGER = 489;
// Settings launched from collapsed quick settings.
@@ -3477,6 +3477,15 @@
// OS: O
WIFI_NETWORK_DETAILS = 849;
+ // ACTION: Settings > Battery > Menu > Usage Alerts
+ ACTION_SETTINGS_MENU_BATTERY_USAGE_ALERTS = 850;
+
+ // ACTION: Settings > Battery > Menu > Optimization
+ ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION = 851;
+
+ // ACTION: Settings > Battery > Menu > Apps Toggle
+ ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 47333cb..e9e73cc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,8 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
import static com.android.server.wm.AppTransition.TRANSIT_NONE;
@@ -144,6 +146,8 @@
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static java.lang.Integer.MAX_VALUE;
+
import android.Manifest;
import android.Manifest.permission;
import android.annotation.NonNull;
@@ -3067,15 +3071,24 @@
startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid);
} else {
finishRunningVoiceLocked();
- IVoiceInteractionSession session;
- if (mLastResumedActivity != null
- && ((session = mLastResumedActivity.task.voiceSession) != null
- || (session = mLastResumedActivity.voiceSession) != null)) {
- // We had been in a voice interaction session, but now focused has
- // move to something different. Just finish the session, we can't
- // return to it and retain the proper state and synchronization with
- // the voice interaction service.
- finishVoiceTask(session);
+
+ if (mLastResumedActivity != null) {
+ final IVoiceInteractionSession session;
+
+ if (mLastResumedActivity.task != null
+ && mLastResumedActivity.task.voiceSession != null) {
+ session = mLastResumedActivity.task.voiceSession;
+ } else {
+ session = mLastResumedActivity.voiceSession;
+ }
+
+ if (session != null) {
+ // We had been in a voice interaction session, but now focused has
+ // move to something different. Just finish the session, we can't
+ // return to it and retain the proper state and synchronization with
+ // the voice interaction service.
+ finishVoiceTask(session);
+ }
}
}
@@ -4148,13 +4161,9 @@
synchronized (this) {
for (int i=mLruProcesses.size()-1; i>=0; i--) {
final ProcessRecord proc = mLruProcesses.get(i);
- if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
- || procState > proc.setProcState) {
- if (proc.pkgList.containsKey(packageName)) {
- procState = proc.setProcState;
- break;
- }
- if (proc.pkgDeps != null && proc.pkgDeps.contains(packageName)) {
+ if (procState > proc.setProcState) {
+ if (proc.pkgList.containsKey(packageName) ||
+ (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
procState = proc.setProcState;
}
}
@@ -9803,14 +9812,19 @@
} else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
stackId = FREEFORM_WORKSPACE_STACK_ID;
}
+
+ // Reparent the task to the right stack if necessary
boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
if (stackId != task.getStackId()) {
- mStackSupervisor.moveTaskToStackUncheckedLocked(task, stackId, ON_TOP,
- !FORCE_FOCUS, "resizeTask");
+ // Defer resume until the task is resized below
+ task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+ DEFER_RESUME, "resizeTask");
preserveWindow = false;
}
- task.resize(bounds, resizeMode, preserveWindow, false /* deferResume */);
+ // After reparenting (which only resizes the task to the stack bounds), resize the
+ // task to the actual bounds provided
+ task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10189,14 +10203,16 @@
throw new IllegalArgumentException(
"exitFreeformMode: No activity record matching token=" + token);
}
- final ActivityStack stack = r.getStackLocked(token);
+
+ final ActivityStack stack = r.getStack();
if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
}
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
- mStackSupervisor.moveTaskToStackLocked(r.task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
- ON_TOP, !FORCE_FOCUS, "exitFreeformMode", ANIMATE);
+ r.task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT,
+ ANIMATE, !DEFER_RESUME, "exitFreeformMode");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10213,15 +10229,22 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+ return;
+ }
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
if (stackId == DOCKED_STACK_ID) {
mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
null /* initialBounds */);
}
- boolean result = mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop,
- !FORCE_FOCUS, "moveTaskToStack", ANIMATE);
- if (result && stackId == DOCKED_STACK_ID) {
+
+ final boolean successful = task.reparent(stackId, toTop,
+ REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack");
+ if (successful && stackId == DOCKED_STACK_ID) {
// If task moved to docked stack - show recents if needed.
mWindowManager.showRecentApps(false /* fromHome */);
}
@@ -10253,22 +10276,23 @@
// TODO: App transition
mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false);
- // Defer the resume so resume/pausing while moving stacks is dangerous.
- mStackSupervisor.moveTaskToStackLocked(topTask.taskId, DOCKED_STACK_ID,
- false /* toTop */, !FORCE_FOCUS, "swapDockedAndFullscreenStack",
- ANIMATE, true /* deferResume */);
+ // Defer the resume until we move all the docked tasks to the fullscreen stack below
+ topTask.reparent(DOCKED_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+ DEFER_RESUME, "swapDockedAndFullscreenStack - DOCKED_STACK");
final int size = tasks.size();
for (int i = 0; i < size; i++) {
final int id = tasks.get(i).taskId;
if (id == topTask.taskId) {
continue;
}
- mStackSupervisor.moveTaskToStackLocked(id,
- FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */, !FORCE_FOCUS,
- "swapDockedAndFullscreenStack", ANIMATE, true /* deferResume */);
+
+ // Defer the resume until after all the tasks have been moved
+ tasks.get(i).reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+ REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, DEFER_RESUME,
+ "swapDockedAndFullscreenStack - FULLSCREEN_STACK");
}
- // Because we deferred the resume, to avoid conflicts with stack switches while
+ // Because we deferred the resume to avoid conflicts with stack switches while
// resuming, we need to do it after all the tasks are moved.
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -10301,12 +10325,20 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
+ return false;
+ }
+
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
- final boolean moved = mStackSupervisor.moveTaskToStackLocked(
- taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
- animate, DEFER_RESUME);
+
+ // Defer resuming until we move the home stack to the front below
+ final boolean moved = task.reparent(DOCKED_STACK_ID, toTop,
+ REPARENT_KEEP_STACK_AT_FRONT, animate, DEFER_RESUME,
+ "moveTaskToDockedStack");
if (moved) {
if (moveHomeStackFront) {
mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
@@ -10440,7 +10472,8 @@
stack.positionChildAt(task, position);
} else {
// Reparent to new stack.
- task.reparent(stackId, position, "positionTaskInStack");
+ task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE,
+ !ANIMATE, !DEFER_RESUME, "positionTaskInStack");
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 672a6ce..c79ed2a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -797,15 +797,21 @@
+ " is already the parent of r=" + this);
}
+ // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
+ // the stacks in strange states. For now, we should use Task.reparent() to ensure that
+ // the stack is left in an OK state.
+ if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
+ throw new IllegalArgumentException(reason + ": task=" + newTask
+ + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
+ + " r=" + this + " (" + prevTask.getStackId() + ")");
+ }
+
// Must reparent first in window manager
mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
// Remove the activity from the old task and add it to the new task
prevTask.removeActivity(this);
- // TODO(b/34179495): This should really be set to null in removeActivity() call above,
- // but really bad things that I can't track down right now happen when I do that.
- // So, setting it here now and will change later when there is time for investigation.
- task = null;
+
newTask.addActivityAtIndex(position, this);
}
@@ -2022,12 +2028,6 @@
// Okay we now are going to make this activity have the new config.
// But then we need to figure out how it needs to deal with that.
-
- // Find changes between last reported merged configuration and the current one. This is used
- // to decide whether to relaunch an activity or just report a configuration change.
- final int changes = getTaskConfigurationChanges(mTmpConfig1);
-
- // Update last reported values.
final Configuration newGlobalConfig = service.getGlobalConfiguration();
final Configuration newTaskMergedOverrideConfig = task.getMergedOverrideConfiguration();
mTmpConfig1.setTo(mLastReportedConfiguration);
@@ -2035,6 +2035,9 @@
mLastReportedConfiguration.setTo(newGlobalConfig);
mLastReportedOverrideConfiguration.setTo(newTaskMergedOverrideConfig);
+ int taskChanges = getTaskConfigurationChanges(this, newTaskMergedOverrideConfig,
+ mTmpConfig2);
+ final int changes = mTmpConfig1.diff(newGlobalConfig) | taskChanges;
if (changes == 0 && !forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration no differences in " + this);
@@ -2049,7 +2052,8 @@
}
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
- "Configuration changes for " + this + ", allChanges="
+ "Configuration changes for " + this + " ; taskChanges="
+ + Configuration.configurationDiffToString(taskChanges) + ", allChanges="
+ Configuration.configurationDiffToString(changes));
// If the activity isn't currently running, just leave the new configuration and it will
@@ -2147,27 +2151,40 @@
return (changes&(~configChanged)) != 0;
}
- private int getTaskConfigurationChanges(Configuration newTaskConfig) {
+ private static int getTaskConfigurationChanges(ActivityRecord record, Configuration taskConfig,
+ Configuration oldTaskOverride) {
+ // If we went from full-screen to non-full-screen, make sure to use the correct
+ // configuration task diff, so the diff stays as small as possible.
+ if (Configuration.EMPTY.equals(oldTaskOverride)
+ && !Configuration.EMPTY.equals(taskConfig)) {
+ oldTaskOverride = record.task.extractOverrideConfig(record.mLastReportedConfiguration);
+ }
+
+ // Conversely, do the same when going the other direction.
+ if (Configuration.EMPTY.equals(taskConfig)
+ && !Configuration.EMPTY.equals(oldTaskOverride)) {
+ taskConfig = record.task.extractOverrideConfig(record.mLastReportedConfiguration);
+ }
+
// Determine what has changed. May be nothing, if this is a config that has come back from
// the app after going idle. In that case we just want to leave the official config object
// now in the activity and do nothing else.
- final Configuration oldTaskConfig = task.getConfiguration();
- int taskChanges = oldTaskConfig.diff(newTaskConfig, true /* skipUndefined */);
+ int taskChanges = oldTaskOverride.diff(taskConfig, true /* skipUndefined */);
// We don't want to use size changes if they don't cross boundaries that are important to
// the app.
if ((taskChanges & CONFIG_SCREEN_SIZE) != 0) {
- final boolean crosses = crossesHorizontalSizeThreshold(oldTaskConfig.screenWidthDp,
- newTaskConfig.screenWidthDp)
- || crossesVerticalSizeThreshold(oldTaskConfig.screenHeightDp,
- newTaskConfig.screenHeightDp);
+ final boolean crosses = record.crossesHorizontalSizeThreshold(
+ oldTaskOverride.screenWidthDp, taskConfig.screenWidthDp)
+ || record.crossesVerticalSizeThreshold(
+ oldTaskOverride.screenHeightDp, taskConfig.screenHeightDp);
if (!crosses) {
taskChanges &= ~CONFIG_SCREEN_SIZE;
}
}
if ((taskChanges & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
- final int oldSmallest = oldTaskConfig.smallestScreenWidthDp;
- final int newSmallest = newTaskConfig.smallestScreenWidthDp;
- if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
+ final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
+ final int newSmallest = taskConfig.smallestScreenWidthDp;
+ if (!record.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
taskChanges &= ~CONFIG_SMALLEST_SCREEN_SIZE;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 09ac9b8..5d4bff9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3827,7 +3827,9 @@
r.app = null;
r.removeWindowContainer();
final TaskRecord task = r.task;
- if (task != null && task.removeActivity(r)) {
+ final boolean lastActivity = task != null ? task.removeActivity(r) : false;
+
+ if (lastActivity) {
if (DEBUG_STACK) Slog.i(TAG_STACK,
"removeActivityFromHistoryLocked: last activity removed from " + this);
if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
@@ -5075,45 +5077,6 @@
moveToFront(reason);
}
- /**
- * Moves the input activity from its current stack to this one.
- * NOTE: The current task of the activity isn't moved to this stack. Instead a new task is
- * created on this stack which the activity is added to.
- * */
- void moveActivityToStack(ActivityRecord r) {
- final ActivityStack prevStack = r.getStack();
- if (prevStack.mStackId == mStackId) {
- // You are already in the right stack silly...
- return;
- }
-
- final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
- && (mStackSupervisor.topRunningActivityLocked() == r);
- final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
- final boolean wasPaused = prevStack.mPausingActivity == r;
-
- // Create a new task for the activity to be parented in
- final TaskRecord task = createTaskRecord(
- mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
- r.info, r.intent, null, null, true, r.mActivityType);
- // This is a new task, so reparenting it to position 0 will move it to the top
- r.reparent(task, 0 /* position */, "moveActivityToStack");
-
- // Notify the task actiivties if it was moved to/from a pinned stack
- mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
-
- // Resume the activity if necessary after it has moved
- moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, wasPaused,
- "moveActivityToStack");
- if (wasResumed) {
- prevStack.mResumedActivity = null;
- }
- if (wasPaused) {
- prevStack.mPausingActivity = null;
- prevStack.removeTimeoutsForActivityLocked(r);
- }
- }
-
public int getStackId() {
return mStackId;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 35145a1..6e138a1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -89,6 +89,9 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
@@ -177,7 +180,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -1993,8 +1995,8 @@
stackId = task.getLaunchStackId();
}
if (stackId != currentStack.mStackId) {
- currentStack = moveTaskToStackUncheckedLocked(task, stackId, ON_TOP,
- !FORCE_FOCUS, reason);
+ task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+ DEFER_RESUME, "findTaskToMoveToFrontLocked");
stackId = currentStack.mStackId;
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
@@ -2279,6 +2281,7 @@
if (onTop) {
for (int i = 0; i < size; i++) {
final TaskRecord task = tasks.get(i);
+ final boolean isTopTask = i == (size - 1);
if (fromStackId == PINNED_STACK_ID) {
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
@@ -2288,22 +2291,25 @@
MetricsLogger.action(mService.mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
}
- moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, onTop, onTop /*forceFocus*/,
- "moveTasksToFullscreenStack - onTop", ANIMATE, DEFER_RESUME);
+ // Defer resume until all the tasks have been moved to the fullscreen stack
+ task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+ REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
+ "moveTasksToFullscreenStack - onTop");
}
-
- ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- resumeFocusedStackTopActivityLocked();
} else {
for (int i = 0; i < size; i++) {
final TaskRecord task = tasks.get(i);
- final int position = fullscreenStack != null ?
- Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0;
+ final int position = fullscreenStack != null
+ ? Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0;
+ // Defer resume until all the tasks have been moved to the fullscreen stack
task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, position,
- "moveTasksToFullscreenStack - NOT_onTop");
+ REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+ DEFER_RESUME, "moveTasksToFullscreenStack - NOT_onTop");
}
}
+
+ ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ resumeFocusedStackTopActivityLocked();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -2446,14 +2452,16 @@
? Math.max(0, fullscreenStack.getChildCount() - 1)
: fullscreenStack.getChildCount();
final TaskRecord task = tasks.get(i);
- task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, insertPosition, "removeStack");
+ // Defer resume until we remove all the tasks
+ task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, insertPosition,
+ REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, "removeStack");
}
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
} else {
// If there is no fullscreen stack, then create the stack and move all the tasks
// onto the stack
- moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+ moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
}
} else {
for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -2658,168 +2666,52 @@
}
/**
- * Moves the specified task record to the input stack id.
- * WARNING: This method performs an unchecked/raw move of the task and
- * can leave the system in an unstable state if used incorrectly.
- * Use {@link #moveTaskToStackLocked} to perform safe task movement to a stack.
- * @param task Task to move.
- * @param stackId Id of stack to move task to.
- * @param toTop True if the task should be placed at the top of the stack.
- * @param forceFocus if focus should be moved to the new stack
- * @param reason Reason the task is been moved.
- * @return The stack the task was moved to.
+ * Returns the reparent target stack, creating the stack if necessary. This call also enforces
+ * the various checks on tasks that are going to be reparented from one stack to another.
*/
- ActivityStack moveTaskToStackUncheckedLocked(TaskRecord task, int stackId, boolean toTop,
- boolean forceFocus, String reason) {
-
- if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
- throw new IllegalStateException("moveTaskToStackUncheckedLocked: Device doesn't "
- + "support multi-window task=" + task + " to stackId=" + stackId);
- }
-
- final ActivityRecord r = task.topRunningActivityLocked();
+ ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) {
final ActivityStack prevStack = task.getStack();
- final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
- final boolean wasResumed = prevStack.mResumedActivity == r;
- final boolean wasPaused = prevStack.mPausingActivity == r;
- // In some cases the focused stack isn't the front stack. E.g. pinned stack.
- // Whenever we are moving the top activity from the front stack we want to make sure to move
- // the stack to the front.
- final boolean wasFront = isFrontStackOnDisplay(prevStack)
- && (prevStack.topRunningActivityLocked() == r);
- if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
- // We don't allow moving a unresizeable task to the docked stack since the docked
- // stack is used for split-screen mode and will cause things like the docked divider to
- // show up. We instead leave the task in its current stack or move it to the fullscreen
- // stack if it isn't currently in a stack.
- stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
- Slog.w(TAG, "Can not move unresizeable task=" + task
- + " to docked stack. Moving to stackId=" + stackId + " instead.");
+ // Check that we aren't reparenting to the same stack that the task is already in
+ if (prevStack != null && prevStack.mStackId == stackId) {
+ Slog.w(TAG, "Can not reparent to same stack, task=" + task
+ + " already in stackId=" + stackId);
+ return prevStack;
}
- // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
- // if a docked stack is created below which will lead to the stack we are moving from and
- // its resizeable tasks being resized.
- task.mTemporarilyUnresizable = true;
- final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
- task.mTemporarilyUnresizable = false;
- task.reparent(stack.mStackId, toTop ? MAX_VALUE : 0, reason);
-
- // Reset the resumed activity on the previous stack
- if (wasResumed) {
- prevStack.mResumedActivity = null;
- }
- // Reset the paused activity on the previous stack
- if (wasPaused) {
- prevStack.mPausingActivity = null;
- prevStack.removeTimeoutsForActivityLocked(r);
+ // Ensure that we aren't trying to move into a multi-window stack without multi-window
+ // support
+ if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
+ throw new IllegalArgumentException("Device doesn't support multi-window, can not"
+ + " reparent task=" + task + " to stackId=" + stackId);
}
- // If the task had focus before (or we're requested to move focus),
- // move focus to the new stack by moving the stack to the front.
- stack.moveToFrontAndResumeStateIfNeeded(r, forceFocus || wasFocused || wasFront, wasResumed,
- wasPaused, reason);
-
- return stack;
- }
-
- boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
- String reason, boolean animate) {
- return moveTaskToStackLocked(taskId, stackId, toTop, forceFocus, reason, animate,
- false /* deferResume */);
- }
-
- boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
- String reason, boolean animate, boolean deferResume) {
- final TaskRecord task = anyTaskForIdLocked(taskId);
- if (task == null) {
- Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
- return false;
- }
-
- final ActivityStack currentStack = task.getStack();
- if (currentStack != null && currentStack.mStackId == stackId) {
- // You are already in the right stack silly...
- Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
- return true;
- }
-
+ // Ensure that we aren't trying to move into a freeform stack without freeform
+ // support
if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
- throw new IllegalArgumentException("moveTaskToStack:"
- + "Attempt to move task " + taskId + " to unsupported freeform stack");
+ throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+ + " task=" + task);
}
- final ActivityRecord topActivity = task.getTopActivity();
- final int sourceStackId = task.getStackId();
- final boolean mightReplaceWindow =
- StackId.replaceWindowsOnTaskMove(sourceStackId, stackId) && topActivity != null;
- if (mightReplaceWindow) {
- // We are about to relaunch the activity because its configuration changed due to
- // being maximized, i.e. size change. The activity will first remove the old window
- // and then add a new one. This call will tell window manager about this, so it can
- // preserve the old window until the new one is drawn. This prevents having a gap
- // between the removal and addition, in which no window is visible. We also want the
- // entrance of the new window to be properly animated.
- // Note here we always set the replacing window first, as the flags might be needed
- // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
- mWindowManager.setWillReplaceWindow(topActivity.appToken, animate);
+ // We don't allow moving a unresizeable task to the docked stack since the docked stack is
+ // used for split-screen mode and will cause things like the docked divider to show up. We
+ // instead leave the task in its current stack or move it to the fullscreen stack if it
+ // isn't currently in a stack.
+ if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
+ stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
+ Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
+ + " Moving to stackId=" + stackId + " instead.");
}
- mWindowManager.deferSurfaceLayout();
- final int preferredLaunchStackId = stackId;
- boolean kept = true;
+ // Temporarily disable resizeablility of the task as we don't want it to be resized if, for
+ // example, a docked stack is created which will lead to the stack we are moving from being
+ // resized and and its resizeable tasks being resized.
try {
- final ActivityStack stack = moveTaskToStackUncheckedLocked(
- task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
- stackId = stack.mStackId;
-
- if (!animate) {
- stack.mNoAnimActivities.add(topActivity);
- }
-
- // We might trigger a configuration change. Save the current task bounds for freezing.
- // TODO: Should this call be moved inside the resize method in WM?
- stack.prepareFreezingTaskBounds();
-
- // Make sure the task has the appropriate bounds/size for the stack it is in.
- if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
- && !Objects.equals(task.mBounds, stack.mBounds)) {
- kept = task.resize(stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
- deferResume);
- } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
- Rect bounds = task.getLaunchBounds();
- if (bounds == null) {
- stack.layoutTaskInStack(task, null);
- bounds = task.mBounds;
- }
- kept = task.resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
- } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- kept = task.resize(stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
- deferResume);
- }
+ task.mTemporarilyUnresizable = true;
+ return getStack(stackId, CREATE_IF_NEEDED, toTop);
} finally {
- mWindowManager.continueSurfaceLayout();
+ task.mTemporarilyUnresizable = false;
}
-
- if (mightReplaceWindow) {
- // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
- // window), we need to clear the replace window settings. Otherwise, we schedule a
- // timeout to remove the old window if the replacing window is not coming in time.
- mWindowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
- }
-
- if (!deferResume) {
-
- // The task might have already been running and its visibility needs to be synchronized with
- // the visibility of the stack / windows.
- ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- resumeFocusedStackTopActivityLocked();
- }
-
- handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId);
-
- return (preferredLaunchStackId == stackId);
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
@@ -2850,8 +2742,8 @@
void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds,
boolean moveHomeStackToFront) {
-
mWindowManager.deferSurfaceLayout();
+
// Need to make sure the pinned stack exist so we can resize it below...
final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
@@ -2883,12 +2775,26 @@
// was launched from home so home should be visible behind it.
moveHomeStackToFront(reason);
}
- moveTaskToStackLocked(
- task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
+ // Defer resume until below
+ task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, reason);
} else {
// There are multiple activities in the task and moving the top activity should
- // reveal/leave the other activities in their original task
- stack.moveActivityToStack(r);
+ // reveal/leave the other activities in their original task.
+
+ // Currently, we don't support reparenting activities across tasks in two different
+ // stacks, so instead, just create a new task in the same stack, reparent the
+ // activity into that task, and then reparent the whole task to the new stack. This
+ // ensures that all the necessary work to migrate states in the old and new stacks
+ // is also done.
+ final TaskRecord newTask = task.getStack().createTaskRecord(
+ getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true,
+ r.mActivityType);
+ r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+ // Defer resume until below
+ newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+ DEFER_RESUME, reason);
}
// Reset the state that indicates it can enter PiP while pausing after we've moved it
@@ -4881,9 +4787,8 @@
if (launchStackId != INVALID_STACK_ID) {
if (task.getStackId() != launchStackId) {
- moveTaskToStackLocked(
- taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
- ANIMATE);
+ task.reparent(launchStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
+ DEFER_RESUME, "startActivityFromRecents");
}
}
@@ -4904,7 +4809,7 @@
setResizingDuringAnimation(task);
}
- mService.mActivityStarter.postStartActivityUncheckedProcessing(task.getTopActivity(),
+ mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(),
ActivityManager.START_TASK_TO_FRONT,
sourceRecord != null ? sourceRecord.task.getStackId() : INVALID_STACK_ID,
sourceRecord, task.getStack());
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index e13d9240..2e33c62 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.Activity.RESULT_CANCELED;
+import static android.app.ActivityManager.START_CANCELED;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
@@ -76,11 +77,16 @@
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -88,12 +94,10 @@
import android.app.AppGlobals;
import android.app.IActivityContainer;
import android.app.IApplicationThread;
-import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.content.ComponentName;
-import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -101,7 +105,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
@@ -509,21 +512,15 @@
doPendingActivityLaunchesLocked(false);
- try {
- mService.mWindowManager.deferSurfaceLayout();
- err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
- true, options, inTask);
- } finally {
- mService.mWindowManager.continueSurfaceLayout();
- }
- postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
- return err;
+ return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
+ options, inTask);
}
- /** Creates a launch intent for the given auxiliary resolution data. */
+ /**
+ * Creates a launch intent for the given auxiliary resolution data.
+ */
private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse,
- Intent originalIntent, String callingPackage,
- String resolvedType, int userId) {
+ Intent originalIntent, String callingPackage, String resolvedType, int userId) {
if (auxiliaryResponse.needsPhaseTwo) {
// request phase two resolution
mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
@@ -535,7 +532,7 @@
auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
}
- void postStartActivityUncheckedProcessing(
+ void postStartActivityProcessing(
ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord,
ActivityStack targetStack) {
@@ -937,6 +934,31 @@
}
}
+ private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+ int result = START_CANCELED;
+ try {
+ mService.mWindowManager.deferSurfaceLayout();
+ result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+ startFlags, doResume, options, inTask);
+ } finally {
+ // If we are not able to proceed, disassociate the activity from the task. Leaving an
+ // activity in an incomplete state can lead to issues, such as performing operations
+ // without a window container.
+ if (result != START_SUCCESS && mStartActivity.task != null) {
+ mStartActivity.task.removeActivity(mStartActivity);
+ }
+ mService.mWindowManager.continueSurfaceLayout();
+ }
+
+ postStartActivityProcessing(r, result, mSupervisor.mFocusedStack.mStackId, mSourceRecord,
+ mTargetStack);
+
+ return result;
+ }
+
+ // Note: This method should only be called from {@link startActivity}.
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
@@ -982,11 +1004,22 @@
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| mLaunchSingleInstance || mLaunchSingleTask) {
+ final TaskRecord task = mReusedActivity.task;
+
// In this situation we want to remove all activities from the task up to the one
// being started. In most cases this means we are resetting the task to its initial
// state.
- final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
- mStartActivity, mLaunchFlags);
+ final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
+ mLaunchFlags);
+
+ // The above code can remove {@code mReusedActivity} from the task, leading to the
+ // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
+ // task reference is needed in the call below to
+ // {@link setTargetStackAndMoveToFrontIfNeeded}.
+ if (mReusedActivity.task == null) {
+ mReusedActivity.task = task;
+ }
+
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different intents for the top activity,
@@ -1461,9 +1494,9 @@
if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
// If we want to launch adjacent and mTargetStack is not the computed
// launch stack - move task to top of computed stack.
- mSupervisor.moveTaskToStackLocked(intentActivity.task.taskId,
- launchStack.mStackId, ON_TOP, FORCE_FOCUS, "launchToSide",
- ANIMATE);
+ intentActivity.task.reparent(launchStack.mStackId, ON_TOP,
+ REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+ "launchToSide");
} else {
// TODO: This should be reevaluated in MW v2.
// We choose to move task to front instead of launching it adjacent
@@ -1678,8 +1711,8 @@
if (mTargetStack == null) {
mTargetStack = sourceStack;
} else if (mTargetStack != sourceStack) {
- mSupervisor.moveTaskToStackLocked(sourceTask.taskId, mTargetStack.mStackId,
- ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
+ sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ !ANIMATE, DEFER_RESUME, "launchToSide");
}
final TaskRecord topTask = mTargetStack.topTask();
@@ -1745,9 +1778,9 @@
mInTask.updateOverrideConfiguration(mLaunchBounds);
int stackId = mInTask.getLaunchStackId();
if (stackId != mInTask.getStackId()) {
- final ActivityStack stack = mSupervisor.moveTaskToStackUncheckedLocked(mInTask,
- stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
- stackId = stack.mStackId;
+ mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+ DEFER_RESUME, "inTaskToFront");
+ stackId = mInTask.getStackId();
}
if (StackId.resizeStackWithLaunchBounds(stackId)) {
mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
@@ -1846,11 +1879,8 @@
final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
try {
- final int result = startActivityUnchecked(
- pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, null);
- postStartActivityUncheckedProcessing(
- pal.r, result, mSupervisor.mFocusedStack.mStackId, mSourceRecord,
- mTargetStack);
+ startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null,
+ null);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f8645d6..3f33f41 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -54,6 +55,7 @@
import com.android.server.wm.StackWindowController;
import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.TaskWindowContainerListener;
+import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,10 +64,13 @@
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -107,9 +112,10 @@
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static java.lang.Integer.MAX_VALUE;
+
final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -160,6 +166,24 @@
static final int INVALID_TASK_ID = -1;
private static final int INVALID_MIN_SIZE = -1;
+ /**
+ * The modes to control how the stack is moved to the front when calling
+ * {@link TaskRecord#reparent}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ REPARENT_MOVE_STACK_TO_FRONT,
+ REPARENT_KEEP_STACK_AT_FRONT,
+ REPARENT_LEAVE_STACK_IN_PLACE
+ })
+ public @interface ReparentMoveStackMode {}
+ // Moves the stack to the front if it was not at the front
+ public static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
+ // Only moves the stack to the front if it was focused or front most already
+ public static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
+ // Do not move the stack as a part of reparenting
+ public static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
@@ -537,36 +561,159 @@
mWindowContainerController.getBounds(bounds);
}
- // TODO: Should we be doing all the stuff in ASS.moveTaskToStackLocked?
- void reparent(int stackId, int position, String reason) {
- mService.mWindowManager.deferSurfaceLayout();
+ /**
+ * Convenience method to reparent a task to the top or bottom position of the stack.
+ */
+ boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
+ boolean animate, boolean deferResume, String reason) {
+ return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+ deferResume, reason);
+ }
+ /**
+ * Reparents the task into a preferred stack, creating it if necessary.
+ *
+ * @param preferredStackId the stack id of the target stack to move this task
+ * @param position the position to place this task in the new stack
+ * @param animate whether or not we should wait for the new window created as a part of the
+ * reparenting to be drawn and animated in
+ * @param moveStackMode whether or not to move the stack to the front always, only if it was
+ * previously focused & in front, or never
+ * @param deferResume whether or not to update the visibility of other tasks and stacks that may
+ * have changed as a result of this reparenting
+ * @param reason the caller of this reparenting
+ * @return
+ */
+ boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
+ boolean animate, boolean deferResume, String reason) {
+ final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+ final WindowManagerService windowManager = mService.mWindowManager;
+ final ActivityStack sourceStack = getStack();
+ final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
+ position == MAX_VALUE);
+ if (toStack == sourceStack) {
+ return false;
+ }
+
+ final int sourceStackId = getStackId();
+ final int stackId = toStack.getStackId();
+ final ActivityRecord topActivity = getTopActivity();
+
+ final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
+ && topActivity != null;
+ if (mightReplaceWindow) {
+ // We are about to relaunch the activity because its configuration changed due to
+ // being maximized, i.e. size change. The activity will first remove the old window
+ // and then add a new one. This call will tell window manager about this, so it can
+ // preserve the old window until the new one is drawn. This prevents having a gap
+ // between the removal and addition, in which no window is visible. We also want the
+ // entrance of the new window to be properly animated.
+ // Note here we always set the replacing window first, as the flags might be needed
+ // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
+ windowManager.setWillReplaceWindow(topActivity.appToken, animate);
+ }
+
+ windowManager.deferSurfaceLayout();
+ boolean kept = true;
try {
- final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
- final ActivityStack newStack = supervisor.getStack(stackId,
- CREATE_IF_NEEDED, false /* toTop */);
+ final ActivityRecord r = topRunningActivityLocked();
+ final boolean wasFocused = supervisor.isFocusedStack(sourceStack)
+ && (topRunningActivityLocked() == r);
+ final boolean wasResumed = sourceStack.mResumedActivity == r;
+ final boolean wasPaused = sourceStack.mPausingActivity == r;
+
+ // In some cases the focused stack isn't the front stack. E.g. pinned stack.
+ // Whenever we are moving the top activity from the front stack we want to make sure to
+ // move the stack to the front.
+ final boolean wasFront = supervisor.isFrontStackOnDisplay(sourceStack)
+ && (sourceStack.topRunningActivityLocked() == r);
+
// Adjust the position for the new parent stack as needed.
- position = newStack.getAdjustedPositionForTask(this, position, null /* starting */);
+ position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
// Must reparent first in window manager to avoid a situation where AM can delete the
// we are coming from in WM before we reparent because it became empty.
- mWindowContainerController.reparent(newStack.getWindowContainerController(), position);
+ mWindowContainerController.reparent(toStack.getWindowContainerController(), position);
- final ActivityStack prevStack = mStack;
- prevStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
- newStack.addTask(this, position, reason);
+ // Reset the resumed activity on the previous stack
+ if (wasResumed) {
+ sourceStack.mResumedActivity = null;
+ }
- supervisor.scheduleReportPictureInPictureModeChangedIfNeeded(this, prevStack);
+ // Reset the paused activity on the previous stack
+ if (wasPaused) {
+ sourceStack.mPausingActivity = null;
+ sourceStack.removeTimeoutsForActivityLocked(r);
+ }
+ // Move the task
+ sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
+ toStack.addTask(this, position, reason);
+
+ // TODO: Ensure that this is actually necessary here
+ // Notify of picture-in-picture mode changes
+ supervisor.scheduleReportPictureInPictureModeChangedIfNeeded(this, sourceStack);
+
+ // TODO: Ensure that this is actually necessary here
+ // Notify the voice session if required
if (voiceSession != null) {
try {
voiceSession.taskStarted(intent, taskId);
} catch (RemoteException e) {
}
}
+
+ // If the task had focus before (or we're requested to move focus), move focus to the
+ // new stack by moving the stack to the front.
+ final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
+ || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
+ toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed, wasPaused,
+ reason);
+ if (!animate) {
+ toStack.mNoAnimActivities.add(topActivity);
+ }
+
+ // We might trigger a configuration change. Save the current task bounds for freezing.
+ // TODO: Should this call be moved inside the resize method in WM?
+ toStack.prepareFreezingTaskBounds();
+
+ // Make sure the task has the appropriate bounds/size for the stack it is in.
+ if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ && !Objects.equals(mBounds, toStack.mBounds)) {
+ kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+ deferResume);
+ } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ Rect bounds = getLaunchBounds();
+ if (bounds == null) {
+ toStack.layoutTaskInStack(this, null);
+ bounds = mBounds;
+ }
+ kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
+ } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
+ kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+ deferResume);
+ }
} finally {
- mService.mWindowManager.continueSurfaceLayout();
+ windowManager.continueSurfaceLayout();
}
+
+ if (mightReplaceWindow) {
+ // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
+ // window), we need to clear the replace window settings. Otherwise, we schedule a
+ // timeout to remove the old window if the replacing window is not coming in time.
+ windowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
+ }
+
+ if (!deferResume) {
+ // The task might have already been running and its visibility needs to be synchronized
+ // with the visibility of the stack / windows.
+ supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
+ supervisor.resumeFocusedStackTopActivityLocked();
+ }
+
+ supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, stackId);
+
+ return (preferredStackId == stackId);
}
void cancelWindowTransition() {
@@ -1081,8 +1228,17 @@
r.onOverrideConfigurationSent();
}
- /** @return true if this was the last activity in the task */
+ /**
+ * @return true if this was the last activity in the task
+ */
boolean removeActivity(ActivityRecord r) {
+ if (r.task != this) {
+ throw new IllegalArgumentException(
+ "Activity=" + r + " does not belong to task=" + this);
+ }
+
+ r.task = null;
+
if (mActivities.remove(r) && r.fullscreen) {
// Was previously in list.
numFullscreen--;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dae5da3..0e767da 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3050,6 +3050,10 @@
throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
+
+ // The system can post notifications for any package, let us resolve that.
+ final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
+
// Fix the notification as best we can.
try {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
@@ -3069,13 +3073,13 @@
channelId = (new Notification.TvExtender(notification)).getChannel();
}
final NotificationChannel channel = mRankingHelper.getNotificationChannelWithFallback(pkg,
- callingUid, channelId, false /* includeDeleted */);
+ notificationUid, channelId, false /* includeDeleted */);
final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, notification,
+ pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
- if (!checkDisqualifyingFeatures(userId, callingUid, id,tag, r)) {
+ if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) {
return;
}
@@ -3101,6 +3105,19 @@
idOut[0] = id;
}
+ private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
+ // The system can post notifications on behalf of any package it wants
+ if (isCallerSystem() && opPackageName != null && !"android".equals(opPackageName)) {
+ try {
+ return getContext().getPackageManager()
+ .getPackageUidAsUser(opPackageName, userId);
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+ return callingUid;
+ }
+
/**
* Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
*
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 867af9a..e72f7ff 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -66,6 +66,7 @@
private static final String ATT_VERSION = "version";
private static final String ATT_NAME = "name";
+ private static final String ATT_NAME_RES_ID = "name_res_id";
private static final String ATT_UID = "uid";
private static final String ATT_ID = "id";
private static final String ATT_PRIORITY = "priority";
@@ -201,12 +202,19 @@
if (TAG_CHANNEL.equals(tagName)) {
String id = parser.getAttributeValue(null, ATT_ID);
CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
+ int channelNameRes = safeInt(parser, ATT_NAME_RES_ID, -1);
int channelImportance =
safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
if (!TextUtils.isEmpty(id)) {
- final NotificationChannel channel = new NotificationChannel(id,
- channelName, channelImportance);
+ NotificationChannel channel;
+ if (channelName != null) {
+ channel = new NotificationChannel(id, channelName,
+ channelImportance);
+ } else {
+ channel = new NotificationChannel(id, channelNameRes,
+ channelImportance);
+ }
channel.populateFromXml(parser);
r.channels.put(id, channel);
}
@@ -286,7 +294,7 @@
NotificationChannel channel;
channel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID,
- mContext.getString(R.string.default_notification_channel_label),
+ R.string.default_notification_channel_label,
r.importance);
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
channel.setLockscreenVisibility(r.visibility);
@@ -480,7 +488,8 @@
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(channel);
Preconditions.checkNotNull(channel.getId());
- Preconditions.checkNotNull(channel.getName());
+ Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())
+ || channel.getNameResId() != 0);
Record r = getOrCreateRecord(pkg, uid);
if (r == null) {
throw new IllegalArgumentException("Invalid package");
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bbd227e..aac04da 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -314,7 +314,8 @@
optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */, false /* checkProfiles */,
getCompilerFilterForReason(compilationReason),
- null /* CompilerStats.PackageStats */);
+ null /* CompilerStats.PackageStats */,
+ mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName));
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b589057..d9ea728 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -104,7 +104,7 @@
*/
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
- CompilerStats.PackageStats packageStats) {
+ CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps) {
if (!canOptimizePackage(pkg)) {
return DEX_OPT_SKIPPED;
}
@@ -119,7 +119,7 @@
}
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
- targetCompilationFilter, packageStats);
+ targetCompilationFilter, packageStats, isUsedByOtherApps);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -135,7 +135,8 @@
@GuardedBy("mInstallLock")
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
String[] targetInstructionSets, boolean checkForProfileUpdates,
- String targetCompilerFilter, CompilerStats.PackageStats packageStats) {
+ String targetCompilerFilter, CompilerStats.PackageStats packageStats,
+ boolean isUsedByOtherApps) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -143,7 +144,7 @@
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
- targetCompilerFilter, isUsedByOtherApps(pkg));
+ targetCompilerFilter, isUsedByOtherApps);
final boolean profileUpdated = checkForProfileUpdates &&
isProfileUpdated(pkg, sharedGid, compilerFilter);
@@ -477,40 +478,6 @@
mSystemReady = true;
}
- /**
- * Returns true if the profiling data collected for the given app indicate
- * that the apps's APK has been loaded by another app.
- * Note that this returns false for all forward-locked apps and apps without
- * any collected profiling data.
- */
- public static boolean isUsedByOtherApps(PackageParser.Package pkg) {
- if (pkg.isForwardLocked()) {
- // Skip the check for forward locked packages since they don't share their code.
- return false;
- }
-
- for (String apkPath : pkg.getAllCodePathsExcludingResourceOnly()) {
- try {
- apkPath = PackageManagerServiceUtils.realpath(new File(apkPath));
- } catch (IOException e) {
- // Log an error but continue without it.
- Slog.w(TAG, "Failed to get canonical path", e);
- continue;
- }
- String useMarker = apkPath.replace('/', '@');
- final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
- for (int i = 0; i < currentUserIds.length; i++) {
- File profileDir =
- Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
- File foreignUseMark = new File(profileDir, useMarker);
- if (foreignUseMark.exists()) {
- return true;
- }
- }
- }
- return false;
- }
-
private String printDexoptFlags(int flags) {
ArrayList<String> flagsList = new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8380983..caacc46 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -746,6 +746,12 @@
@GuardedBy("mPackages")
final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
+ final PackageParser.Callback mPackageParserCallback = new PackageParser.Callback() {
+ @Override public boolean hasFeature(String feature) {
+ return PackageManagerService.this.hasSystemFeature(feature, 0);
+ }
+ };
+
public static final class SharedLibraryEntry {
public final String path;
public final String apk;
@@ -7584,7 +7590,7 @@
+ " flags=0x" + Integer.toHexString(parseFlags));
}
ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
- mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir);
+ mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
// Submit files for parsing in parallel
int fileCount = 0;
@@ -7753,6 +7759,7 @@
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
+ pp.setCallback(mPackageParserCallback);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
@@ -8434,11 +8441,13 @@
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
false /* checkProfiles */,
getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY),
- getOrCreateCompilerPackageStats(depPackage));
+ getOrCreateCompilerPackageStats(depPackage),
+ mDexManager.isUsedByOtherApps(p.packageName));
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
- targetCompilerFilter, getOrCreateCompilerPackageStats(p));
+ targetCompilerFilter, getOrCreateCompilerPackageStats(p),
+ mDexManager.isUsedByOtherApps(p.packageName));
}
// Performs dexopt on the used secondary dex files belonging to the given package.
@@ -8710,6 +8719,7 @@
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
+ mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
}
}
@@ -9616,7 +9626,7 @@
/**
* Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackgeManagerException}.
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
* <p>
* Implementation detail: This method must NOT have any side effects. It would
* ideally be static, but, it requires locks to read system state.
@@ -12814,7 +12824,7 @@
* By having a field variable, we're able to track filter ordering as soon as
* a non-zero order is defined. Otherwise, multiple loops across the result set
* would be needed to apply ordering. If the intent resolver becomes re-entrant,
- * this needs to be contained entirely within {@link #filterResults()}.
+ * this needs to be contained entirely within {@link #filterResults}.
*/
final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
@@ -16125,6 +16135,8 @@
setInstantAppForUser(ps, user.getIdentifier(), instantApp, fullApp);
prepareAppDataAfterInstallLIF(newPackage);
addedPkg = true;
+ mDexManager.notifyPackageUpdated(newPackage.packageName,
+ newPackage.baseCodePath, newPackage.splitCodePaths);
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -16274,6 +16286,9 @@
updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
installReason);
prepareAppDataAfterInstallLIF(newPackage);
+
+ mDexManager.notifyPackageUpdated(newPackage.packageName,
+ newPackage.baseCodePath, newPackage.splitCodePaths);
}
} catch (PackageManagerException e) {
res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
@@ -16636,6 +16651,7 @@
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
+ pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
@@ -16648,13 +16664,19 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
-// // Ephemeral apps must have target SDK >= O.
-// // TODO: Update conditional and error message when O gets locked down
-// if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
-// res.setError(PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID,
-// "Ephemeral apps must have target SDK version of at least O");
-// return;
-// }
+ // Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
+ if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " does not target O, this will be a fatal error.");
+ // STOPSHIP: Make this a fatal error
+ pkg.applicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+ }
+ if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
+ Slog.w(TAG, "Instant app package " + pkg.packageName
+ + " does not target targetSandboxVersion 2, this will be a fatal error.");
+ // STOPSHIP: Make this a fatal error
+ pkg.applicationInfo.targetSandboxVersion = 2;
+ }
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static shared libraries have synthetic package names
@@ -16951,7 +16973,8 @@
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */, false /* checkProfiles */,
getCompilerFilterForReason(REASON_INSTALL),
- getOrCreateCompilerPackageStats(pkg));
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.isUsedByOtherApps(pkg.packageName));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Notify BackgroundDexOptJobService that the package has been changed.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 9c9a671..0634dac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -80,9 +80,7 @@
try {
// Check that the system property name is legal.
String sysPropName = getSystemPropertyName(reason);
- if (sysPropName == null ||
- sysPropName.isEmpty() ||
- sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
+ if (sysPropName == null || sysPropName.isEmpty()) {
throw new IllegalStateException("Reason system property name \"" +
sysPropName +"\" for reason " + REASON_STRINGS[reason]);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 45887e1..9feee8c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -133,7 +133,8 @@
sortTemp, packageManagerService);
// Give priority to apps used by other apps.
- applyPackageFilter((pkg) -> PackageDexOptimizer.isUsedByOtherApps(pkg), result,
+ applyPackageFilter((pkg) ->
+ packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 6033855..4ff3e12 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -46,6 +46,7 @@
private final boolean mOnlyCore;
private final DisplayMetrics mMetrics;
private final File mCacheDir;
+ private final PackageParser.Callback mPackageParserCallback;
private volatile String mInterruptedInThread;
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
@@ -54,11 +55,12 @@
"package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
- DisplayMetrics metrics, File cacheDir) {
+ DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
mSeparateProcesses = separateProcesses;
mOnlyCore = onlyCoreApps;
mMetrics = metrics;
mCacheDir = cacheDir;
+ mPackageParserCallback = callback;
}
static class ParseResult {
@@ -110,6 +112,7 @@
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
+ pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 00f3711..755c486 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageParser;
import android.os.RemoteException;
import android.os.storage.StorageManager;
+import android.os.UserHandle;
import android.util.Slog;
@@ -179,17 +180,64 @@
}
}
- public void notifyPackageInstalled(PackageInfo info, int userId) {
- cachePackageCodeLocation(info, userId);
+ /**
+ * Notifies that a new package was installed for {@code userId}.
+ * {@code userId} must not be {@code UserHandle.USER_ALL}.
+ *
+ * @throws IllegalArgumentException if {@code userId} is {@code UserHandle.USER_ALL}.
+ */
+ public void notifyPackageInstalled(PackageInfo pi, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ throw new IllegalArgumentException(
+ "notifyPackageInstalled called with USER_ALL");
+ }
+ cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
+ pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
}
- private void cachePackageCodeLocation(PackageInfo info, int userId) {
- PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
- if (pcl != null) {
- pcl.mergeAppDataDirs(info.applicationInfo, userId);
- } else {
- mPackageCodeLocationsCache.put(info.packageName,
- new PackageCodeLocations(info.applicationInfo, userId));
+ /**
+ * Notifies that package {@code packageName} was updated.
+ * This will clear the UsedByOtherApps mark if it exists.
+ */
+ public void notifyPackageUpdated(String packageName, String baseCodePath,
+ String[] splitCodePaths) {
+ cachePackageCodeLocation(packageName, baseCodePath, splitCodePaths, null, /*userId*/ -1);
+ // In case there was an update, write the package use info to disk async.
+ // Note that we do the writing here and not in PackageDexUsage in order to be
+ // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
+ // multiple updates in PackaeDexUsage before writing it).
+ if (mPackageDexUsage.clearUsedByOtherApps(packageName)) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ }
+
+ /**
+ * Notifies that the user {@code userId} data for package {@code packageName}
+ * was destroyed. This will remove all usage info associated with the package
+ * for the given user.
+ * {@code userId} is allowed to be {@code UserHandle.USER_ALL} in which case
+ * all usage information for the package will be removed.
+ */
+ public void notifyPackageDataDestroyed(String packageName, int userId) {
+ boolean updated = userId == UserHandle.USER_ALL
+ ? mPackageDexUsage.removePackage(packageName)
+ : mPackageDexUsage.removeUserPackage(packageName, userId);
+ // In case there was an update, write the package use info to disk async.
+ // Note that we do the writing here and not in PackageDexUsage in order to be
+ // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
+ // multiple updates in PackaeDexUsage before writing it).
+ if (updated) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ }
+
+ public void cachePackageCodeLocation(String packageName, String baseCodePath,
+ String[] splitCodePaths, String dataDir, int userId) {
+ PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
+ new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
+ pcl.updateCodeLocation(baseCodePath, splitCodePaths);
+ if (dataDir != null) {
+ pcl.mergeAppDataDirs(dataDir, userId);
}
}
@@ -202,7 +250,8 @@
int userId = entry.getKey();
for (PackageInfo pi : packageInfoList) {
// Cache the code locations.
- cachePackageCodeLocation(pi, userId);
+ cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
+ pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
// Cache a map from package name to the set of user ids who installed the package.
// We will use it to sync the data and remove obsolete entries from
@@ -359,6 +408,23 @@
}
/**
+ * Return true if the profiling data collected for the given app indicate
+ * that the apps's APK has been loaded by another app.
+ * Note that this returns false for all apps without any collected profiling data.
+ */
+ public boolean isUsedByOtherApps(String packageName) {
+ PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ if (useInfo == null) {
+ // No use info, means the package was not used or it was used but not by other apps.
+ // Note that right now we might prune packages which are not used by other apps.
+ // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ // to access the package use.
+ return false;
+ }
+ return useInfo.isUsedByOtherApps();
+ }
+
+ /**
* Retrieves the package which owns the given dexPath.
*/
private DexSearchResult getDexPackage(
@@ -408,27 +474,36 @@
*/
private static class PackageCodeLocations {
private final String mPackageName;
- private final String mBaseCodePath;
+ private String mBaseCodePath;
private final Set<String> mSplitCodePaths;
// Maps user id to the application private directory.
private final Map<Integer, Set<String>> mAppDataDirs;
public PackageCodeLocations(ApplicationInfo ai, int userId) {
- mPackageName = ai.packageName;
- mBaseCodePath = ai.sourceDir;
+ this(ai.packageName, ai.sourceDir, ai.splitSourceDirs);
+ mergeAppDataDirs(ai.dataDir, userId);
+ }
+ public PackageCodeLocations(String packageName, String baseCodePath,
+ String[] splitCodePaths) {
+ mPackageName = packageName;
mSplitCodePaths = new HashSet<>();
- if (ai.splitSourceDirs != null) {
- for (String split : ai.splitSourceDirs) {
+ mAppDataDirs = new HashMap<>();
+ updateCodeLocation(baseCodePath, splitCodePaths);
+ }
+
+ public void updateCodeLocation(String baseCodePath, String[] splitCodePaths) {
+ mBaseCodePath = baseCodePath;
+ mSplitCodePaths.clear();
+ if (splitCodePaths != null) {
+ for (String split : splitCodePaths) {
mSplitCodePaths.add(split);
}
}
- mAppDataDirs = new HashMap<>();
- mergeAppDataDirs(ai, userId);
}
- public void mergeAppDataDirs(ApplicationInfo ai, int userId) {
+ public void mergeAppDataDirs(String dataDir, int userId) {
Set<String> dataDirs = putIfAbsent(mAppDataDirs, userId, new HashSet<>());
- dataDirs.add(ai.dataDir);
+ dataDirs.add(dataDir);
}
public int searchDex(String dexPath, int userId) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 3693bce0..8a66f12 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -377,7 +377,34 @@
}
/**
+ * Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
+ * @return true if the package usage info was updated.
+ */
+ public boolean clearUsedByOtherApps(String packageName) {
+ synchronized (mPackageUseInfoMap) {
+ PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
+ if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+ return false;
+ }
+ packageUseInfo.mIsUsedByOtherApps = false;
+ return true;
+ }
+ }
+
+ /**
+ * Remove the usage data associated with package {@code packageName}.
+ * @return true if the package usage was found and removed successfully.
+ */
+ public boolean removePackage(String packageName) {
+ synchronized (mPackageUseInfoMap) {
+ return mPackageUseInfoMap.remove(packageName) != null;
+ }
+ }
+
+ /**
* Remove all the records about package {@code packageName} belonging to user {@code userId}.
+ * If the package is left with no records of secondary dex usage and is not used by other
+ * apps it will be removed as well.
* @return true if the record was found and actually deleted,
* false if the record doesn't exist
*/
@@ -397,6 +424,12 @@
updated = true;
}
}
+ // If no secondary dex info is left and the package is not used by other apps
+ // remove the data since it is now useless.
+ if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+ mPackageUseInfoMap.remove(packageName);
+ updated = true;
+ }
return updated;
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 994f38d..f5ccf08 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -795,16 +795,6 @@
if (canFreezeBounds()) {
freezeBounds();
}
-
- // In the process of tearing down before relaunching, the app will
- // try and clean up it's child surfaces. We need to prevent this from
- // happening, so we sever the children, transfering their ownership
- // from the client it-self to the parent surface (owned by us).
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.mWinAnimator.detachChildren();
- }
-
mPendingRelaunchCount++;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb3a2d1..e339329 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2199,15 +2199,6 @@
if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
-
- // When we start the exit animation we take the Surface from the client
- // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger
- // side child surfaces, so they will remain preserved in their current state
- // (rather than be cleaned up immediately by the app code).
- SurfaceControl.openTransaction();
- winAnimator.detachChildren();
- SurfaceControl.closeTransaction();
-
return focusMayChange;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 4b71338..98598e1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -566,20 +566,6 @@
if (!mDestroyPreservedSurfaceUponRedraw) {
return;
}
- if (mSurfaceController != null) {
- if (mPendingDestroySurface != null) {
- // If we are preserving a surface but we aren't relaunching that means
- // we are just doing an in-place switch. In that case any SurfaceFlinger side
- // child layers need to be reparented to the new surface to make this
- // transparent to the app.
- if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) {
- SurfaceControl.openTransaction();
- mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController);
- SurfaceControl.closeTransaction();
- }
- }
- }
-
destroyDeferredSurfaceLocked();
mDestroyPreservedSurfaceUponRedraw = false;
}
@@ -1979,10 +1965,4 @@
}
return mForceScaleUntilResize;
}
-
- void detachChildren() {
- if (mSurfaceController != null) {
- mSurfaceController.detachChildren();
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7d3343..f8e7428 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -135,20 +135,6 @@
}
}
- void reparentChildrenInTransaction(WindowSurfaceController other) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other);
- if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
- mSurfaceControl.reparentChildren(other.getHandle());
- }
- }
-
- void detachChildren() {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN");
- if (mSurfaceControl != null) {
- mSurfaceControl.detachChildren();
- }
- }
-
void hideInTransaction(String reason) {
if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
mHiddenForOtherReasons = true;
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 6212626..ffb0a9e 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -196,6 +196,7 @@
private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getNameResId(), actual.getNameResId());
assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
assertEquals(expected.getImportance(), actual.getImportance());
@@ -260,11 +261,13 @@
@Test
public void testChannelXml() throws Exception {
+ int nameResId = 924896;
+
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "2");
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ new NotificationChannel("id2", nameResId, IMPORTANCE_LOW);
channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
channel2.enableLights(true);
channel2.setBypassDnd(true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
index 6c6eb7e..d665094 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -69,7 +69,7 @@
class TestParallelPackageParser extends ParallelPackageParser {
TestParallelPackageParser() {
- super(null, false, null, null);
+ super(null, false, null, null, null);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 90a2ec0..fa0bd39 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -16,9 +16,10 @@
package com.android.server.pm.dex;
-import android.os.Build;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -57,6 +58,7 @@
private int mUser0;
private int mUser1;
+
@Before
public void setup() {
@@ -243,6 +245,113 @@
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
+ @Test
+ public void testNotifyPackageUpdated() {
+ // Foo loads Bar main apks.
+ notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
+
+ // Bar is used by others now and should be in our records.
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertNotNull(pui);
+ assertTrue(pui.isUsedByOtherApps());
+ assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+ // Notify that bar is updated.
+ mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
+ mBarUser0.mPackageInfo.applicationInfo.sourceDir,
+ mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
+
+ // The usedByOtherApps flag should be clear now.
+ pui = getPackageUseInfo(mBarUser0);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ }
+
+ @Test
+ public void testNotifyPackageUpdatedCodeLocations() {
+ // Simulate a split update.
+ String newSplit = mBarUser0.replaceLastSplit();
+ List<String> newSplits = new ArrayList<>();
+ newSplits.add(newSplit);
+
+ // We shouldn't find yet the new split as we didn't notify the package update.
+ notifyDexLoad(mFooUser0, newSplits, mUser0);
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertNull(pui);
+
+ // Notify that bar is updated. splitSourceDirs will contain the updated path.
+ mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
+ mBarUser0.mPackageInfo.applicationInfo.sourceDir,
+ mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
+
+ // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
+ notifyDexLoad(mFooUser0, newSplits, mUser0);
+ pui = getPackageUseInfo(mBarUser0);
+ assertNotNull(pui);
+ assertTrue(pui.isUsedByOtherApps());
+ }
+
+ @Test
+ public void testNotifyPackageDataDestroyForOne() {
+ // Bar loads its own secondary files.
+ notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
+ notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
+
+ mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
+
+ // Bar should not be around since it was removed for all users.
+ PackageUseInfo pui = getPackageUseInfo(mBarUser1);
+ assertNotNull(pui);
+ assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
+ /*isUsedByOtherApps*/false, mUser1);
+ }
+
+ @Test
+ public void testNotifyPackageDataDestroyForeignUse() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ // Bar loads Foo main apks.
+ notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
+
+ mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
+
+ // Foo should still be around since it's used by other apps but with no
+ // secondary dex info.
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertNotNull(pui);
+ assertTrue(pui.isUsedByOtherApps());
+ assertTrue(pui.getDexUseInfoMap().isEmpty());
+ }
+
+ @Test
+ public void testNotifyPackageDataDestroyComplete() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
+
+ // Foo should not be around since all its secondary dex info were deleted
+ // and it is not used by other apps.
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertNull(pui);
+ }
+
+ @Test
+ public void testNotifyPackageDataDestroyForAll() {
+ // Foo loads its own secondary files.
+ notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
+ notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
+
+ mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
+
+ // Bar should not be around since it was removed for all users.
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertNull(pui);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
for (String dex : secondaries) {
@@ -317,5 +426,12 @@
}
return paths;
}
+
+ String replaceLastSplit() {
+ int length = mPackageInfo.applicationInfo.splitSourceDirs.length;
+ // Add an extra bogus dex extension to simulate a new split name.
+ mPackageInfo.applicationInfo.splitSourceDirs[length - 1] += ".dex";
+ return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 19e0bcf..2e99433 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -257,6 +257,30 @@
}
@Test
+ public void testRemovePackage() {
+ // Record Bar secondaries for two different users.
+ assertTrue(record(mBarSecondary1User0));
+ assertTrue(record(mBarSecondary2User1));
+
+ // Remove the package.
+ assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+ // Assert that we can't find the package anymore.
+ assertNull(mPackageDexUsage.getPackageUseInfo(mBarSecondary1User0.mPackageName));
+ }
+
+ @Test
+ public void testRemoveNonexistentPackage() {
+ // Record Bar secondaries for two different users.
+ assertTrue(record(mBarSecondary1User0));
+
+ // Remove the package.
+ assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+ // Remove the package again. It should return false because the package no longer
+ // has a record in the use info.
+ assertFalse(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+ }
+
+ @Test
public void testRemoveUserPackage() {
// Record Bar secondaries for two different users.
assertTrue(record(mBarSecondary1User0));
@@ -282,6 +306,32 @@
assertPackageDexUsage(null, mBarSecondary2User1);
}
+ @Test
+ public void testClearUsedByOtherApps() {
+ // Write a package which is used by other apps.
+ assertTrue(record(mFooSplit2UsedByOtherApps0));
+ assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+
+ // Check that the package is no longer used by other apps.
+ TestData noLongerUsedByOtherApps = new TestData(
+ mFooSplit2UsedByOtherApps0.mPackageName,
+ mFooSplit2UsedByOtherApps0.mDexFile,
+ mFooSplit2UsedByOtherApps0.mOwnerUserId,
+ mFooSplit2UsedByOtherApps0.mLoaderIsa,
+ /*mIsUsedByOtherApps*/false,
+ mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
+ assertPackageDexUsage(noLongerUsedByOtherApps);
+ }
+
+ @Test
+ public void testClearUsedByOtherAppsNonexistent() {
+ // Write a package which is used by other apps.
+ assertTrue(record(mFooSplit2UsedByOtherApps0));
+ assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+ // Clearing again should return false as there should be no update on the use info.
+ assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+ }
+
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6d465f7..932c276 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4041,9 +4041,8 @@
}
}
- if (property.length() > SystemProperties.PROP_NAME_MAX
- || propVal.length() > SystemProperties.PROP_VALUE_MAX) {
- Rlog.d(TAG, "setTelephonyProperty: property to long phoneId=" + phoneId +
+ if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+ Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
" property=" + property + " value: " + value + " propVal=" + propVal);
return;
}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0e031e7..15648bd 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -309,6 +309,8 @@
CATEGORY_ATTR = 0x010103e8,
BANNER_ATTR = 0x10103f2,
ISGAME_ATTR = 0x10103f4,
+ REQUIRED_FEATURE_ATTR = 0x1010557,
+ REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
};
String8 getComponentName(String8 &pkgName, String8 &componentName) {
@@ -366,11 +368,19 @@
printf("\n");
}
-static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
+static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
+ const String8& requiredFeature = String8::empty(),
+ const String8& requiredNotFeature = String8::empty()) {
printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
if (maxSdkVersion != -1) {
printf(" maxSdkVersion='%d'", maxSdkVersion);
}
+ if (requiredFeature.length() > 0) {
+ printf(" requiredFeature='%s'", requiredFeature.string());
+ }
+ if (requiredNotFeature.length() > 0) {
+ printf(" requiredNotFeature='%s'", requiredNotFeature.string());
+ }
printf("\n");
if (optional) {
@@ -1545,6 +1555,10 @@
const int32_t maxSdkVersion =
AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
+ const String8 requiredFeature = AaptXml::getAttribute(tree,
+ REQUIRED_FEATURE_ATTR, &error);
+ const String8 requiredNotFeature = AaptXml::getAttribute(tree,
+ REQUIRED_NOT_FEATURE_ATTR, &error);
if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
hasWriteExternalStoragePermission = true;
@@ -1565,7 +1579,7 @@
printUsesPermission(name,
AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
- maxSdkVersion);
+ maxSdkVersion, requiredFeature, requiredNotFeature);
} else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index fad35d2..8130bc2 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -52,6 +52,8 @@
throw new IllegalArgumentException("Outline is not a rect shadow");
}
+ // TODO replacing the algorithm here to create better shadow
+
float shadowSize = elevationToShadow(elevation);
int saved = modifyCanvas(canvas, shadowSize);
if (saved == -1) {
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
new file mode 100644
index 0000000..4f3ed60
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
new file mode 100644
index 0000000..59dbbec
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1">
+
+ <Button
+ android:layout_marginLeft="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:elevation="48dp"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:layout_marginRight="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:elevation="48dp"
+ android:stateListAnimator="@null"/>
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1">
+
+ <Button
+ android:layout_marginLeft="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:elevation="0dp"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:layout_marginRight="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:elevation="100dp"
+ android:stateListAnimator="@null"/>
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1">
+
+ <Button
+ android:layout_marginLeft="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:elevation="12dp"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:layout_marginRight="40dp"
+ android:layout_width="48dp"
+ android:layout_height="40dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:elevation="36dp"
+ android:stateListAnimator="@null"/>
+
+ </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 9e60f0f..7199781 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -424,4 +424,9 @@
assertNotEquals(0, outValue.data);
assertTrue(sRenderMessages.isEmpty());
}
+
+ @Test
+ public void testRectangleShadow() throws Exception {
+ renderAndVerify("shadows_test.xml", "shadows_test.png");
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 620759d..2388841 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -122,12 +122,22 @@
private static final int MAX_PASSWORD_BYTES = 255;
/**
+ * Supported authentication methods.
+ * @hide
+ */
+ public static final String AUTH_METHOD_PAP = "PAP";
+ /** @hide */
+ public static final String AUTH_METHOD_MSCHAP = "MS-CHAP";
+ /** @hide */
+ public static final String AUTH_METHOD_MSCHAPV2 = "MS-CHAP-V2";
+
+ /**
* Supported Non-EAP inner methods. Refer to
* Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical
* Specification Section 9.1 for more info.
*/
- private static final Set<String> SUPPORTED_AUTH =
- new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2"));
+ private static final Set<String> SUPPORTED_AUTH = new HashSet<String>(
+ Arrays.asList(AUTH_METHOD_PAP, AUTH_METHOD_MSCHAP, AUTH_METHOD_MSCHAPV2));
/**
* Username of the credential.
@@ -348,8 +358,9 @@
public static final class CertificateCredential implements Parcelable {
/**
* Supported certificate types.
+ * @hide
*/
- private static final String CERT_TYPE_X509V3 = "x509v3";
+ public static final String CERT_TYPE_X509V3 = "x509v3";
/**
* Certificate SHA-256 fingerprint length.