Merge "Show misc channel with pre-upgrade fields" into oc-dev
diff --git a/api/removed.txt b/api/removed.txt
index e7b573b..f52b39a 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -73,7 +73,6 @@
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
- field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
}
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 15de5c4..9f3970d 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -71,7 +71,6 @@
method public deprecated android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
method public deprecated android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
- field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
}
}
diff --git a/api/test-removed.txt b/api/test-removed.txt
index e7b573b..f52b39a 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -73,7 +73,6 @@
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
- field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
}
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 588a1bf..b60aed6 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -51,6 +51,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -347,7 +348,7 @@
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 000420f..5fedc9e 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -84,6 +84,11 @@
int main(int argc, char** argv)
{
+ // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
+ // not allowed to spawn any additional threads, but we still spawn
+ // a binder thread from userspace when we call startThreadPool().
+ // See b/36066697 for rationale
+ ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
const char* pname = argv[0];
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6fa0a6d..a8183f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -976,7 +976,7 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
- if (getApplicationInfo().targetSdkVersion >= O && mActivityInfo.isFixedOrientation()) {
+ if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
ta.recycle();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0dfaf6a..e9ee1386 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -169,7 +169,8 @@
* Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
* such as Power Save mode.
*/
- public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration);
+ public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
+ IBinder whitelistToken, long duration);
/**
* Allow DeviceIdleController to tell us about what apps are whitelisted.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 424e783..2de205b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -65,6 +65,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -376,7 +377,9 @@
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
if (isCredentialProtectedStorage()
- && !getSystemService(UserManager.class).isUserUnlocked() && !isBuggy()) {
+ && !getSystemService(StorageManager.class).isUserKeyUnlocked(
+ UserHandle.myUserId())
+ && !isBuggy()) {
throw new IllegalStateException("SharedPreferences in credential encrypted "
+ "storage are not available until after user is unlocked");
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 68fce75..9552d17 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -562,8 +562,8 @@
void notifyLockedProfile(int userId);
void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
void sendIdleJobTrigger();
- int sendIntentSender(in IIntentSender target, int code, in Intent intent,
- in String resolvedType, in IIntentReceiver finishedReceiver,
+ int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
+ in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
in String requiredPermission, in Bundle options);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 06509ae..4294eab 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -42,8 +42,10 @@
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.BadParcelableException;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -835,6 +837,22 @@
public ArraySet<PendingIntent> allPendingIntents;
/**
+ * Token identifying the notification that is applying doze/bgcheck whitelisting to the
+ * pending intents inside of it, so only those will get the behavior.
+ *
+ * @hide
+ */
+ static public IBinder whitelistToken;
+
+ /**
+ * Must be set by a process to start associating tokens with Notification objects
+ * coming in to it. This is set by NotificationManagerService.
+ *
+ * @hide
+ */
+ static public IBinder processWhitelistToken;
+
+ /**
* {@link #extras} key: this is the title of the notification,
* as supplied to {@link Builder#setContentTitle(CharSequence)}.
*/
@@ -1823,6 +1841,13 @@
{
int version = parcel.readInt();
+ whitelistToken = parcel.readStrongBinder();
+ if (whitelistToken == null) {
+ whitelistToken = processWhitelistToken;
+ }
+ // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ parcel.setClassCookie(PendingIntent.class, whitelistToken);
+
when = parcel.readLong();
creationTime = parcel.readLong();
if (parcel.readInt() != 0) {
@@ -1929,6 +1954,7 @@
* @hide
*/
public void cloneInto(Notification that, boolean heavy) {
+ that.whitelistToken = this.whitelistToken;
that.when = this.when;
that.creationTime = this.creationTime;
that.mSmallIcon = this.mSmallIcon;
@@ -2158,6 +2184,7 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
+ parcel.writeStrongBinder(whitelistToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 704e912..bc7fcf5 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -101,21 +101,11 @@
/**
* @hide
*/
- public static final int USER_LOCKED_ALLOWED = 0x00000040;
-
- /**
- * @hide
- */
public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
/**
* @hide
*/
- public static final int USER_LOCKED_AUDIO_ATTRIBUTES = 0x00000100;
-
- /**
- * @hide
- */
public static final int[] LOCKABLE_FIELDS = new int[] {
USER_LOCKED_PRIORITY,
USER_LOCKED_VISIBILITY,
@@ -123,9 +113,7 @@
USER_LOCKED_LIGHTS,
USER_LOCKED_VIBRATION,
USER_LOCKED_SOUND,
- USER_LOCKED_ALLOWED,
USER_LOCKED_SHOW_BADGE,
- USER_LOCKED_AUDIO_ATTRIBUTES
};
private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -273,6 +261,13 @@
/**
* @hide
*/
+ public void unlockFields(int field) {
+ mUserLockedFields &= ~field;
+ }
+
+ /**
+ * @hide
+ */
public void setDeleted(boolean deleted) {
mDeleted = deleted;
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index dc432af..11d9b5e 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -93,6 +93,7 @@
*/
public final class PendingIntent implements Parcelable {
private final IIntentSender mTarget;
+ private IBinder mWhitelistToken;
/** @hide */
@IntDef(flag = true,
@@ -656,7 +657,7 @@
*
*/
public IntentSender getIntentSender() {
- return new IntentSender(mTarget);
+ return new IntentSender(mTarget, mWhitelistToken);
}
/**
@@ -870,7 +871,7 @@
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = ActivityManager.getService().sendIntentSender(
- mTarget, code, intent, resolvedType,
+ mTarget, mWhitelistToken, code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
@@ -1085,7 +1086,9 @@
= new Parcelable.Creator<PendingIntent>() {
public PendingIntent createFromParcel(Parcel in) {
IBinder target = in.readStrongBinder();
- return target != null ? new PendingIntent(target) : null;
+ return target != null
+ ? new PendingIntent(target, in.getClassCookie(PendingIntent.class))
+ : null;
}
public PendingIntent[] newArray(int size) {
@@ -1108,31 +1111,39 @@
}
/**
- * Convenience function for reading either a Messenger or null pointer from
- * a Parcel. You must have previously written the Messenger with
+ * Convenience function for reading either a PendingIntent or null pointer from
+ * a Parcel. You must have previously written the PendingIntent with
* {@link #writePendingIntentOrNullToParcel}.
*
- * @param in The Parcel containing the written Messenger.
+ * @param in The Parcel containing the written PendingIntent.
*
- * @return Returns the Messenger read from the Parcel, or null if null had
+ * @return Returns the PendingIntent read from the Parcel, or null if null had
* been written.
*/
@Nullable
public static PendingIntent readPendingIntentOrNullFromParcel(@NonNull Parcel in) {
IBinder b = in.readStrongBinder();
- return b != null ? new PendingIntent(b) : null;
+ return b != null ? new PendingIntent(b, in.getClassCookie(PendingIntent.class)) : null;
}
/*package*/ PendingIntent(IIntentSender target) {
mTarget = target;
}
- /*package*/ PendingIntent(IBinder target) {
+ /*package*/ PendingIntent(IBinder target, Object cookie) {
mTarget = IIntentSender.Stub.asInterface(target);
+ if (cookie != null) {
+ mWhitelistToken = (IBinder)cookie;
+ }
}
/** @hide */
public IIntentSender getTarget() {
return mTarget;
}
+
+ /** @hide */
+ public IBinder getWhitelistToken() {
+ return mWhitelistToken;
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 109b5bb..de80c36 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3038,13 +3038,6 @@
*/
public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1;
- /**
- * Instead use {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}.
- * @removed
- */
- @Deprecated
- public static final int FLAG_EVICT_CE_KEY = 1;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag=true, value={FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY})
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index 45c62d4..21ea2fe 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -22,6 +22,6 @@
/** @hide */
oneway interface IIntentSender {
- void send(int code, in Intent intent, String resolvedType,
+ void send(int code, in Intent intent, String resolvedType, in IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options);
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 4adb5b7..0a456b5 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -56,6 +56,7 @@
*/
public class IntentSender implements Parcelable {
private final IIntentSender mTarget;
+ IBinder mWhitelistToken;
/**
* Exception thrown when trying to send through a PendingIntent that
@@ -187,7 +188,7 @@
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
- int res = ActivityManager.getService().sendIntentSender(mTarget,
+ int res = ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken,
code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
@@ -365,6 +366,12 @@
}
/** @hide */
+ public IntentSender(IIntentSender target, IBinder whitelistToken) {
+ mTarget = target;
+ mWhitelistToken = whitelistToken;
+ }
+
+ /** @hide */
public IntentSender(IBinder target) {
mTarget = IIntentSender.Stub.asInterface(target);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2468d9..1a4a1e5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -157,6 +157,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_INSTANT,
+ MATCH_STATIC_SHARED_LIBRARIES,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
})
@@ -177,6 +178,7 @@
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
MATCH_INSTANT,
+ MATCH_STATIC_SHARED_LIBRARIES,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -475,6 +477,16 @@
public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
/**
+ * Internal {@link PackageInfo} flag: include static shared libraries.
+ * Apps that depend on static shared libs can always access the version
+ * of the lib they depend on. System/shell/root can access all shared
+ * libs regardless of dependency but need to explicitly ask for them
+ * via this flag.
+ * @hide
+ */
+ public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 0b0f048..f33c751 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -103,7 +103,8 @@
}
/**
- * Return the number of indices in the array that actually have data.
+ * Returns the number of indices in the array that actually have data. Attributes with a value
+ * of @empty are included, as this is an explicit indicator.
*
* @throws RuntimeException if the TypedArray has already been recycled.
*/
@@ -116,7 +117,8 @@
}
/**
- * Returns an index in the array that has data.
+ * Returns an index in the array that has data. Attributes with a value of @empty are included,
+ * as this is an explicit indicator.
*
* @param at The index you would like to returned, ranging from 0 to
* {@link #getIndexCount()}.
@@ -1017,7 +1019,7 @@
* @param outValue TypedValue object in which to place the attribute's
* data.
*
- * @return {@code true} if the value was retrieved, false otherwise.
+ * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise.
* @throws RuntimeException if the TypedArray has already been recycled.
*/
public boolean getValue(@StyleableRes int index, TypedValue outValue) {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 65025fb..8d85880 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1548,6 +1548,7 @@
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
+ p.adoptClassCookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index c5c743b..38a5395 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -204,6 +204,8 @@
private boolean mOwnsNativeParcelObject;
private long mNativeSize;
+ private ArrayMap<Class, Object> mClassCookies;
+
private RuntimeException mStack;
private static final int POOL_SIZE = 6;
@@ -497,6 +499,24 @@
return nativeCompareData(mNativePtr, other.mNativePtr);
}
+ /** @hide */
+ public final void setClassCookie(Class clz, Object cookie) {
+ if (mClassCookies == null) {
+ mClassCookies = new ArrayMap<>();
+ }
+ mClassCookies.put(clz, cookie);
+ }
+
+ /** @hide */
+ public final Object getClassCookie(Class clz) {
+ return mClassCookies != null ? mClassCookies.get(clz) : null;
+ }
+
+ /** @hide */
+ public final void adoptClassCookies(Parcel from) {
+ mClassCookies = from.mClassCookies;
+ }
+
/**
* Report whether the parcel contains any marshalled file descriptors.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cf44c7d..ba41933 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6946,6 +6946,13 @@
public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
/**
+ * Whether the launcher should show any notification badges.
+ * The value is boolean (1 or 0).
+ * @hide
+ */
+ public static final String NOTIFICATION_BADGING = "notification_badging";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -7040,7 +7047,8 @@
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
ASSIST_GESTURE_ENABLED,
ASSIST_GESTURE_SENSITIVITY,
- VR_DISPLAY_MODE
+ VR_DISPLAY_MODE,
+ NOTIFICATION_BADGING
};
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d8eb950..07258fc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2858,6 +2858,14 @@
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
+ /**
+ * Flag indicating that when layout is completed we should notify
+ * that the view was entered for autofill purposes. To minimize
+ * showing autofill for views not visible to the user we evaluate
+ * user visibility which cannot be done until the view is laid out.
+ */
+ static final int PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT = 0x4000;
+
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
static final int SCROLL_INDICATORS_NONE = 0x0000;
@@ -6496,7 +6504,7 @@
if (mParent != null) {
mParent.requestChildFocus(this, this);
- setFocusedInCluster();
+ updateFocusedInCluster(oldFocus, direction);
}
if (mAttachInfo != null) {
@@ -6845,8 +6853,15 @@
if (isAutofillable() && isAttachedToWindow()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
- if (enter && hasWindowFocus() && isFocused() && isVisibleToUser()) {
- afm.notifyViewEntered(this);
+ if (enter && hasWindowFocus() && isFocused()) {
+ // We have not been laid out yet, hence cannot evaluate
+ // whether this view is visible to the user, we will do
+ // the evaluation once layout is complete.
+ if (!isLaidOut()) {
+ mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
+ } else if (isVisibleToUser()) {
+ afm.notifyViewEntered(this);
+ }
} else if (!hasWindowFocus() || !isFocused()) {
afm.notifyViewExited(this);
}
@@ -9912,22 +9927,47 @@
* @hide
*/
public final void setFocusedInCluster() {
- View top = findKeyboardNavigationCluster();
- if (top == this) {
+ setFocusedInCluster(findKeyboardNavigationCluster());
+ }
+
+ private void setFocusedInCluster(View cluster) {
+ if (this instanceof ViewGroup) {
+ ((ViewGroup) this).mFocusedInCluster = null;
+ }
+ if (cluster == this) {
return;
}
ViewParent parent = mParent;
View child = this;
while (parent instanceof ViewGroup) {
- ((ViewGroup) parent).setFocusedInCluster(child);
- if (parent == top) {
- return;
+ ((ViewGroup) parent).mFocusedInCluster = child;
+ if (parent == cluster) {
+ break;
}
child = (View) parent;
parent = parent.getParent();
}
}
+ private void updateFocusedInCluster(View oldFocus, @FocusDirection int direction) {
+ if (oldFocus != null) {
+ View oldCluster = oldFocus.findKeyboardNavigationCluster();
+ View cluster = findKeyboardNavigationCluster();
+ if (oldCluster != cluster) {
+ // Going from one cluster to another, so save last-focused.
+ // This covers cluster jumps because they are always FOCUS_DOWN
+ oldFocus.setFocusedInCluster(oldCluster);
+ if (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) {
+ // This is a result of ordered navigation so consider navigation through
+ // the previous cluster "complete" and clear its last-focused memory.
+ if (oldFocus.mParent instanceof ViewGroup) {
+ ((ViewGroup) oldFocus.mParent).clearFocusedInCluster(oldFocus);
+ }
+ }
+ }
+ }
+ }
+
/**
* Returns whether this View should receive focus when the focus is restored for the view
* hierarchy containing this view.
@@ -19305,6 +19345,11 @@
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
+
+ if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
+ mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
+ notifyEnterOrExitForAutoFillIfNeeded(true);
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 18c1b8c..50593f2 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -142,7 +142,7 @@
// that is or contains a default-focus view.
private View mDefaultFocus;
// The last child of this ViewGroup which held focus within the current cluster
- private View mFocusedInCluster;
+ View mFocusedInCluster;
/**
* A Transformation used when drawing children, to
@@ -806,10 +806,6 @@
return mDefaultFocus != null || super.hasDefaultFocus();
}
- void setFocusedInCluster(View child) {
- mFocusedInCluster = child;
- }
-
/**
* Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
* it.
@@ -825,8 +821,11 @@
ViewParent parent = this;
do {
((ViewGroup) parent).mFocusedInCluster = null;
+ if (parent == top) {
+ break;
+ }
parent = parent.getParent();
- } while (parent != top && parent instanceof ViewGroup);
+ } while (parent instanceof ViewGroup);
}
@Override
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 1f3be84..32fae73 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -140,4 +140,14 @@
@WorkerThread
LinksInfo getLinks(
@NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales);
+
+ /**
+ * Logs a TextClassifier event.
+ *
+ * @param source the text classifier used to generate this event
+ * @param event the text classifier related event
+ * @hide
+ */
+ @WorkerThread
+ default void logEvent(String source, String event) {}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7362c70..5f72fc7 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,6 +40,7 @@
import android.widget.TextViewMetrics;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -77,6 +78,8 @@
private final Context mContext;
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
private final Object mSmartSelectionLock = new Object();
@GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
private Map<Locale, String> mModelFilePaths;
@@ -105,7 +108,8 @@
if (start <= end
&& start >= 0 && end <= string.length()
&& start <= selectionStartIndex && end >= selectionEndIndex) {
- final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
+ final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end)
+ .setLogSource(LOG_TAG);
final SmartSelection.ClassificationResult[] results =
smartSelection.classifyText(
string, start, end,
@@ -173,6 +177,13 @@
return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
}
+ @Override
+ public void logEvent(String source, String event) {
+ if (LOG_TAG.equals(source)) {
+ mMetricsLogger.count(event, 1);
+ }
+ }
+
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mSmartSelectionLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 3172c13..9a66693 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -34,13 +34,16 @@
private final int mEndIndex;
@NonNull private final EntityConfidence<String> mEntityConfidence;
@NonNull private final List<String> mEntities;
+ @NonNull private final String mLogSource;
private TextSelection(
- int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) {
+ int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
+ @NonNull String logSource) {
mStartIndex = startIndex;
mEndIndex = endIndex;
mEntityConfidence = new EntityConfidence<>(entityConfidence);
mEntities = mEntityConfidence.getEntities();
+ mLogSource = logSource;
}
/**
@@ -87,6 +90,14 @@
return mEntityConfidence.getConfidenceScore(entity);
}
+ /**
+ * Returns a tag for the source classifier used to generate this result.
+ * @hide
+ */
+ public String getSourceClassifier() {
+ return mLogSource;
+ }
+
@Override
public String toString() {
return String.format("TextSelection {%d, %d, %s}",
@@ -102,6 +113,7 @@
private final int mEndIndex;
@NonNull private final EntityConfidence<String> mEntityConfidence =
new EntityConfidence<>();
+ @NonNull private String mLogSource = "";
/**
* Creates a builder used to build {@link TextSelection} objects.
@@ -131,10 +143,19 @@
}
/**
+ * Sets a tag for the source classifier used to generate this result.
+ * @hide
+ */
+ Builder setLogSource(@NonNull String logSource) {
+ mLogSource = Preconditions.checkNotNull(logSource);
+ return this;
+ }
+
+ /**
* Builds and returns {@link TextSelection} object.
*/
public TextSelection build() {
- return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence);
+ return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence, mLogSource);
}
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b0d6395..bb658c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3925,6 +3925,8 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ getSelectionActionModeHelper().onSelectionAction();
+
if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
return true;
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 16a1087..89182b0 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -56,13 +56,14 @@
private TextClassification mTextClassification;
private AsyncTask mTextClassificationAsyncTask;
- private final SelectionInfo mSelectionInfo = new SelectionInfo();
+ private final SelectionTracker mSelectionTracker;
SelectionActionModeHelper(@NonNull Editor editor) {
mEditor = Preconditions.checkNotNull(editor);
final TextView textView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
+ mSelectionTracker = new SelectionTracker(textView.getTextClassifier());
}
public void startActionModeAsync(boolean adjustSelection) {
@@ -99,8 +100,13 @@
}
}
+ public void onSelectionAction() {
+ mSelectionTracker.onSelectionAction(mTextClassificationHelper.getClassifierTag());
+ }
+
public boolean resetSelection(int textIndex) {
- if (mSelectionInfo.resetSelection(textIndex, mEditor)) {
+ if (mSelectionTracker.resetSelection(
+ textIndex, mEditor, mTextClassificationHelper.getClassifierTag())) {
invalidateActionModeAsync();
return true;
}
@@ -113,7 +119,7 @@
}
public void onDestroyActionMode() {
- mSelectionInfo.onSelectionDestroyed();
+ mSelectionTracker.onSelectionDestroyed();
cancelAsyncTask();
}
@@ -137,7 +143,7 @@
private void startActionMode(@Nullable SelectionResult result) {
final TextView textView = mEditor.getTextView();
final CharSequence text = textView.getText();
- mSelectionInfo.setOriginalSelection(
+ mSelectionTracker.setOriginalSelection(
textView.getSelectionStart(), textView.getSelectionEnd());
if (result != null && text instanceof Spannable) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
@@ -151,7 +157,8 @@
controller.show();
}
if (result != null) {
- mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd);
+ mSelectionTracker.onSelectionStarted(
+ result.mStart, result.mEnd, mTextClassificationHelper.getClassifierTag());
}
}
mEditor.setRestartActionModeOnNextRefresh(false);
@@ -165,7 +172,9 @@
actionMode.invalidate();
}
final TextView textView = mEditor.getTextView();
- mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd());
+ mSelectionTracker.onSelectionUpdated(
+ textView.getSelectionStart(), textView.getSelectionEnd(),
+ mTextClassificationHelper.getClassifierTag());
mTextClassificationAsyncTask = null;
}
@@ -177,49 +186,111 @@
}
/**
- * Holds information about the selection and uses it to decide on whether or not to update
- * the selection when resetSelection is called.
- * The expected UX here is to allow the user to select a word inside of the "smart selection" on
- * a single tap.
+ * Tracks and logs smart selection changes.
+ * It is important to trigger this object's methods at the appropriate event so that it tracks
+ * smart selection events appropriately.
*/
- private static final class SelectionInfo {
+ private static final class SelectionTracker {
+
+ // Log event: Smart selection happened.
+ private static final String LOG_EVENT_MULTI_SELECTION =
+ "textClassifier_multiSelection";
+
+ // Log event: Smart selection acted upon.
+ private static final String LOG_EVENT_MULTI_SELECTION_ACTION =
+ "textClassifier_multiSelection_action";
+
+ // Log event: Smart selection was reset to original selection.
+ private static final String LOG_EVENT_MULTI_SELECTION_RESET =
+ "textClassifier_multiSelection_reset";
+
+ // Log event: Smart selection was user modified.
+ private static final String LOG_EVENT_MULTI_SELECTION_MODIFIED =
+ "textClassifier_multiSelection_modified";
+
+ private final TextClassifier mClassifier;
private int mOriginalStart;
private int mOriginalEnd;
private int mSelectionStart;
private int mSelectionEnd;
- private boolean mResetOriginal;
+ private boolean mSmartSelectionActive;
+ SelectionTracker(TextClassifier classifier) {
+ mClassifier = classifier;
+ }
+
+ /**
+ * Called to initialize the original selection before smart selection is triggered.
+ */
public void setOriginalSelection(int selectionStart, int selectionEnd) {
mOriginalStart = selectionStart;
mOriginalEnd = selectionEnd;
- mResetOriginal = false;
+ mSmartSelectionActive = false;
}
- public void onSelectionStarted(int selectionStart, int selectionEnd) {
- // Set the reset flag to true if the selection changed.
+ /**
+ * Called when selection action mode is started.
+ * If the selection indices are different from the original selection indices, we have a
+ * smart selection.
+ */
+ public void onSelectionStarted(int selectionStart, int selectionEnd, String logTag) {
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
- mResetOriginal = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ // If the started selection is different from the original selection, we have a
+ // smart selection.
+ mSmartSelectionActive =
+ mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION);
+ }
}
- public void onSelectionUpdated(int selectionStart, int selectionEnd) {
- // If the selection did not change, maintain the reset state. Otherwise, disable reset.
- mResetOriginal &= selectionStart == mSelectionStart && selectionEnd == mSelectionEnd;
+ /**
+ * Called when selection bounds change.
+ */
+ public void onSelectionUpdated(int selectionStart, int selectionEnd, String logTag) {
+ final boolean selectionChanged =
+ selectionStart != mSelectionStart || selectionEnd != mSelectionEnd;
+ if (selectionChanged) {
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_MODIFIED);
+ }
+ mSmartSelectionActive = false;
+ }
}
+ /**
+ * Called when the selection action mode is destroyed.
+ */
public void onSelectionDestroyed() {
- mResetOriginal = false;
+ mSmartSelectionActive = false;
}
- public boolean resetSelection(int textIndex, Editor editor) {
+ /**
+ * Logs if the action was taken on a smart selection.
+ */
+ public void onSelectionAction(String logTag) {
+ if (mSmartSelectionActive) {
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_ACTION);
+ }
+ }
+
+ /**
+ * Returns true if the current smart selection should be reset to normal selection based on
+ * information that has been recorded about the original selection and the smart selection.
+ * The expected UX here is to allow the user to select a word inside of the smart selection
+ * on a single tap.
+ */
+ public boolean resetSelection(int textIndex, Editor editor, String logTag) {
final CharSequence text = editor.getTextView().getText();
- if (mResetOriginal
+ if (mSmartSelectionActive
&& textIndex >= mSelectionStart && textIndex <= mSelectionEnd
&& text instanceof Spannable) {
// Only allow a reset once.
- mResetOriginal = false;
+ mSmartSelectionActive = false;
+ mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_RESET);
return editor.selectCurrentWord();
}
return false;
@@ -301,6 +372,7 @@
/** End index relative to mText. */
private int mSelectionEnd;
private LocaleList mLocales;
+ private String mClassifierTag = "";
/** Trimmed text starting from mTrimStart in mText. */
private CharSequence mTrimmedText;
@@ -364,9 +436,14 @@
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
+ mClassifierTag = sel.getSourceClassifier();
return classifyText();
}
+ String getClassifierTag() {
+ return mClassifierTag;
+ }
+
private void trimText() {
mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index fef85da..797cf2b 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -40,6 +40,7 @@
public static String UPDATES = "UPDATES";
public static String NETWORK_STATUS = "NETWORK_STATUS";
public static String NETWORK_ALERTS = "NETWORK_ALERTS";
+ public static String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
public static String VPN = "VPN";
public static String DEVICE_ADMIN = "DEVICE_ADMIN";
public static String ALERTS = "ALERTS";
@@ -99,6 +100,11 @@
channelsList.add(networkAlertsChannel);
channelsList.add(new NotificationChannel(
+ NETWORK_AVAILABLE,
+ context.getString(R.string.notification_channel_network_available),
+ NotificationManager.IMPORTANCE_LOW));
+
+ channelsList.add(new NotificationChannel(
VPN,
context.getString(R.string.notification_channel_vpn),
NotificationManager.IMPORTANCE_LOW));
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 748272600..6b18591 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -9812,10 +9812,9 @@
}
});
- // TODO: STOPSHIP, remove the "true" below after b/34961340 is fixed
- if (DEBUG_ENERGY_CPU || true) {
- Slog.d(TAG, "Reading cpu stats took " + (mClocks.elapsedRealtime() - startTimeMs) +
- " ms");
+ final long elapse = (mClocks.elapsedRealtime() - startTimeMs);
+ if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
+ Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
}
if (mOnBatteryInternal && numWakelocks > 0) {
diff --git a/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
index b5915aa..aa56e93 100644
--- a/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
+++ b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
@@ -8,6 +8,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.BufferedReader;
+import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
@@ -24,14 +25,25 @@
public class KernelMemoryBandwidthStats {
private static final String TAG = "KernelMemoryBandwidthStats";
- final protected LongSparseLongArray mBandwidthEntries = new LongSparseLongArray();
private static final String mSysfsFile = "/sys/kernel/memory_state_time/show_stat";
private static final boolean DEBUG = false;
+ protected final LongSparseLongArray mBandwidthEntries = new LongSparseLongArray();
+ private boolean mStatsDoNotExist = false;
+
public void updateStats() {
+ if (mStatsDoNotExist) {
+ // Skip reading.
+ return;
+ }
+
StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
try (BufferedReader reader = new BufferedReader(new FileReader(mSysfsFile))) {
parseStats(reader);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "No kernel memory bandwidth stats available");
+ mBandwidthEntries.clear();
+ mStatsDoNotExist = true;
} catch (IOException e) {
Slog.e(TAG, "Failed to read memory bandwidth: " + e.getMessage());
mBandwidthEntries.clear();
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index ea40fd5..8b73daf 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -503,6 +503,7 @@
SettingProto demo_user_setup_complete = 165;
SettingProto instant_apps_enabled = 166;
SettingProto device_paired = 167;
+ SettingProto notification_badging = 168;
}
message SystemSettingsProto {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b7e8467..6640102 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1013,6 +1013,9 @@
<!-- Is the notification LED intrusive? Used to decide if there should be a disable option -->
<bool name="config_intrusiveNotificationLed">false</bool>
+ <!-- De we do icon badges? Used to decide if there should be a disable option-->
+ <bool name="config_notificationBadging">true</bool>
+
<!-- Default value for LED off time when the battery is low on charge in miliseconds -->
<integer name="config_notificationsBatteryLedOff">2875</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb1851b..98356a2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -629,6 +629,9 @@
<!-- Text shown when viewing channel settings for notifications related to network alerts -->
<string name="notification_channel_network_alerts">Network alerts</string>
+ <!-- Text shown when viewing the channel settings for notification about open nearby wireless networks. -->
+ <string name="notification_channel_network_available">Network available</string>
+
<!-- Text shown when viewing channel settings for notifications related to vpn status -->
<string name="notification_channel_vpn">VPN status</string>
@@ -2993,6 +2996,17 @@
<!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
<string name="ringtone_unknown">Unknown</string>
+ <!-- A notification is shown when there are open wireless networks nearby. This is the notification's title. -->
+ <plurals name="wifi_available">
+ <item quantity="one">Wi-Fi network available</item>
+ <item quantity="other">Wi-Fi networks available</item>
+ </plurals>
+ <!-- A notification is shown when there are open wireless networks nearby. This is the notification's message. -->
+ <plurals name="wifi_available_detailed">
+ <item quantity="one">Open Wi-Fi network available</item>
+ <item quantity="other">Open Wi-Fi networks available</item>
+ </plurals>
+
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
<string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 31d13c9..74779f3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1861,6 +1861,8 @@
<java-symbol type="layout" name="safe_mode" />
<java-symbol type="layout" name="simple_list_item_2_single_choice" />
<java-symbol type="layout" name="app_error_dialog" />
+ <java-symbol type="plurals" name="wifi_available" />
+ <java-symbol type="plurals" name="wifi_available_detailed" />
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
@@ -2087,6 +2089,7 @@
<java-symbol type="string" name="config_wifi_tether_enable" />
<java-symbol type="integer" name="config_wifi_wakeup_available" />
<java-symbol type="bool" name="config_intrusiveNotificationLed" />
+ <java-symbol type="bool" name="config_notificationBadging" />
<java-symbol type="dimen" name="preference_fragment_padding_bottom" />
<java-symbol type="dimen" name="preference_fragment_padding_side" />
<java-symbol type="drawable" name="expander_ic_maximized" />
@@ -2969,6 +2972,7 @@
<java-symbol type="string" name="notification_channel_updates" />
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
+ <java-symbol type="string" name="notification_channel_network_available" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
index 1567046..15dbddf 100644
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
@@ -25,11 +25,11 @@
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.UserHandle;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import java.io.File;
@@ -48,14 +48,14 @@
public final long WAIT_TIME_INCR=10*1000;
private static final long THRESHOLD=5;
private static final long ACTUAL_THRESHOLD=10;
-
+
@Override
protected void setUp() throws Exception {
super.setUp();
if(localLOGV) Log.i(TAG, "Cleaning up cache directory first");
cleanUpCacheDirectory();
}
-
+
void cleanUpDirectory(File pDir, String dirName) {
File testDir = new File(pDir, dirName);
if(!testDir.exists()) {
@@ -72,13 +72,13 @@
}
testDir.delete();
}
-
+
void cleanUpCacheDirectory() {
File testDir = mContext.getCacheDir();
if(!testDir.exists()) {
return;
}
-
+
String fList[] = testDir.list();
if(fList == null) {
testDir.delete();
@@ -93,7 +93,7 @@
}
}
}
-
+
@SmallTest
public void testDeleteAllCacheFiles() {
String testName="testDeleteAllCacheFiles";
@@ -160,9 +160,9 @@
+(blks1-blks3));
}
}
-
+
/**
- * This method opens an output file writes to it, opens the same file as an input
+ * This method opens an output file writes to it, opens the same file as an input
* stream, reads the contents and verifies the data that was written earlier can be read
*/
public void openOutFileInAppFilesDir(File pFile, String pFileOut) {
@@ -180,7 +180,7 @@
failStr(e.getMessage());
} catch (IOException e) {
failStr(e.getMessage());
- }
+ }
int count = pFileOut.getBytes().length;
byte[] buffer = new byte[count];
try {
@@ -194,8 +194,8 @@
}
String str = new String(buffer);
assertEquals(str, pFileOut);
- }
-
+ }
+
/*
* This test case verifies that output written to a file
* using Context.openFileOutput has executed successfully.
@@ -215,7 +215,7 @@
failStr(e);
}
}
-
+
@SmallTest
public void testAppCacheCreateFile() {
String fileName = "testFile1.txt";
@@ -225,7 +225,7 @@
openOutFileInAppFilesDir(file, fileOut);
cleanUpCacheDirectory();
}
-
+
@MediumTest
public void testAppCreateCacheFiles() {
File cacheDir = mContext.getCacheDir();
@@ -261,7 +261,7 @@
}
}
}
-
+
byte[] getBuffer() {
String sbuffer = "a";
for(int i = 0; i < 10; i++) {
@@ -360,7 +360,7 @@
}
assertTrue("Files should have been removed", removedFlag);
}
-
+
//createTestFiles(new File(super.getContext().getCacheDir(), "testtmp", "dir", 3)
void createTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
byte buffer[] = getBuffer();
@@ -439,7 +439,7 @@
}
}
}
-
+
class PackageDataObserver extends IPackageDataObserver.Stub {
public boolean retValue = false;
private boolean doneFlag = false;
@@ -455,11 +455,11 @@
return doneFlag;
}
}
-
+
IPackageManager getPm() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
-
+
boolean invokePMDeleteAppCacheFiles() throws Exception {
try {
String packageName = mContext.getPackageName();
@@ -485,7 +485,7 @@
return false;
}
}
-
+
boolean invokePMFreeApplicationCache(long idealStorageSize) throws Exception {
try {
String packageName = mContext.getPackageName();
@@ -512,7 +512,7 @@
}
}
- boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r,
+ boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r,
PendingIntent pi) throws Exception {
try {
// Spin lock waiting for call back
@@ -536,7 +536,7 @@
return false;
}
}
-
+
@LargeTest
public void testDeleteAppCacheFiles() throws Exception {
String testName="testDeleteAppCacheFiles";
@@ -551,7 +551,7 @@
public boolean retValue = false;
public PackageStats stats;
private boolean doneFlag = false;
-
+
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
synchronized(this) {
@@ -565,7 +565,7 @@
return doneFlag;
}
}
-
+
public PackageStats invokePMGetPackageSizeInfo() throws Exception {
try {
String packageName = mContext.getPackageName();
@@ -593,7 +593,7 @@
return null;
}
}
-
+
@SmallTest
public void testGetPackageSizeInfo() throws Exception {
String testName="testGetPackageSizeInfo";
@@ -603,7 +603,7 @@
if(localLOGV) Log.i(TAG, "code="+stats.codeSize+", data="+stats.dataSize+
", cache="+stats.cacheSize);
}
-
+
@SmallTest
public void testGetSystemSharedLibraryNames() throws Exception {
try {
@@ -615,17 +615,17 @@
}
} catch (RemoteException e) {
fail("Failed invoking getSystemSharedLibraryNames with exception:" + e);
- }
+ }
}
-
+
class FreeStorageReceiver extends BroadcastReceiver {
public static final String ACTION_FREE = "com.android.unit_tests.testcallback";
private boolean doneFlag = false;
-
+
public boolean isDone() {
return doneFlag;
}
-
+
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase(ACTION_FREE)) {
@@ -637,7 +637,7 @@
}
}
}
-
+
// TODO: flaky test, omit from LargeTest for now
//@LargeTest
public void testFreeStorage() throws Exception {
@@ -664,10 +664,10 @@
if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after freeing cache"+blks3);
assertEquals(receiver.getResultCode(), 1);
mContext.unregisterReceiver(receiver);
- // Verify result
+ // Verify result
verifyTestFiles1(cacheDir, "testtmpdir", 5);
}
-
+
/* utility method used to create observer and check async call back from PackageManager.
* ClearApplicationUserData
*/
@@ -696,7 +696,7 @@
return false;
}
}
-
+
void verifyUserDataCleared(File pDir) {
if(localLOGV) Log.i(TAG, "Verifying "+pDir);
if(pDir == null) {
@@ -717,7 +717,7 @@
fail(pDir+" should be empty or contain only lib subdirectory. Found "+fileList[i]);
}
}
-
+
File getDataDir() {
try {
ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
@@ -727,7 +727,8 @@
throw new RuntimeException("Pacakge manager dead", e);
}
}
-
+
+ @Suppress
@LargeTest
public void testClearApplicationUserDataWithTestData() throws Exception {
File cacheDir = mContext.getCacheDir();
@@ -740,14 +741,16 @@
//confirm files dont exist
verifyUserDataCleared(getDataDir());
}
-
+
+ @Suppress
@SmallTest
public void testClearApplicationUserDataWithNoTestData() throws Exception {
assertTrue(invokePMClearApplicationUserData());
//confirm files dont exist
verifyUserDataCleared(getDataDir());
}
-
+
+ @Suppress
@LargeTest
public void testClearApplicationUserDataNoObserver() throws Exception {
getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
@@ -756,5 +759,5 @@
//confirm files dont exist
verifyUserDataCleared(getDataDir());
}
-
+
}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 86ab3dc..344f3c8 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -181,9 +181,6 @@
<allow-in-power-save package="com.android.cellbroadcastreceiver" />
<allow-in-power-save package="com.android.shell" />
- <!-- STOPSHIP(b/36856786): Revert this once it is fixed properly -->
- <allow-in-power-save package="com.google.android.apps.enterprise.dmagent" />
-
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 2771ade..60e3845 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -44,8 +44,7 @@
};
class BagAttributeFinder
- : public BackTrackingAttributeFinder<BagAttributeFinder,
- const ResTable::bag_entry*> {
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
public:
BagAttributeFinder(const ResTable::bag_entry* start,
const ResTable::bag_entry* end)
@@ -76,8 +75,7 @@
uint32_t def_style_bag_type_set_flags = 0;
if (def_style_attr != 0) {
Res_value value;
- if (theme->getAttribute(def_style_attr, &value,
- &def_style_bag_type_set_flags) >= 0) {
+ if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = value.data;
}
@@ -127,18 +125,14 @@
ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
value.data);
}
- }
-
- if (value.dataType == Res_value::TYPE_NULL) {
- const ResTable::bag_entry* const def_style_entry =
- def_style_attr_finder.Find(cur_ident);
+ } else {
+ const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
if (def_style_entry != def_style_end) {
block = def_style_entry->stringBlock;
type_set_flags = def_style_type_set_flags;
value = def_style_entry->map.value;
if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
@@ -146,29 +140,24 @@
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block = theme->resolveAttributeReference(
- &value, block, &resid, &type_set_flags, &config);
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
if (new_block >= 0) block = new_block;
if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- } else {
+ } else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find
// it in the theme!
- ssize_t new_block =
- theme->getAttribute(cur_ident, &value, &type_set_flags);
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
if (new_block >= 0) {
if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid,
- &type_set_flags, &config);
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
if (new_block >= 0) block = new_block;
if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
@@ -184,8 +173,7 @@
}
if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
- value.dataType, value.data);
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
// Write the final value back to Java.
@@ -198,7 +186,8 @@
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
- if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+ if (out_indices != nullptr &&
+ (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
indices_idx++;
out_indices[indices_idx] = ii;
}
@@ -247,8 +236,7 @@
ssize_t idx = xml_parser->indexOfStyle();
if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
if (value.dataType == value.TYPE_ATTRIBUTE) {
- if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) <
- 0) {
+ if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
value.dataType = Res_value::TYPE_NULL;
}
}
@@ -318,41 +306,34 @@
// We found the attribute we were looking for.
xml_parser->getAttributeValue(xml_attr_idx, &value);
if (kDebugStyles) {
- ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the style class values looking for the requested
- // attribute.
- const ResTable::bag_entry* const style_attr_entry =
- style_attr_finder.Find(cur_ident);
+ if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ // Walk through the style class values looking for the requested attribute.
+ const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
if (style_attr_entry != style_attr_end) {
// We found the attribute we were looking for.
block = style_attr_entry->stringBlock;
type_set_flags = style_type_set_flags;
value = style_attr_entry->map.value;
if (kDebugStyles) {
- ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the default style values looking for the requested
- // attribute.
- const ResTable::bag_entry* const def_style_attr_entry =
- def_style_attr_finder.Find(cur_ident);
+ if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ // Walk through the default style values looking for the requested attribute.
+ const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
if (def_style_attr_entry != def_style_attr_end) {
// We found the attribute we were looking for.
block = def_style_attr_entry->stringBlock;
type_set_flags = style_type_set_flags;
value = def_style_attr_entry->map.value;
if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
@@ -360,35 +341,29 @@
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ssize_t new_block = theme->resolveAttributeReference(
- &value, block, &resid, &type_set_flags, &config);
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
if (new_block >= 0) {
block = new_block;
}
if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- } else {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t new_block =
- theme->getAttribute(cur_ident, &value, &type_set_flags);
+ } else if (value.data != Res_value::DATA_NULL_EMPTY) {
+ // If we still don't have a value for this attribute, try to find it in the theme!
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
if (new_block >= 0) {
if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_block = res.resolveReference(&value, new_block, &resid,
- &type_set_flags, &config);
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
if (new_block >= 0) {
block = new_block;
}
if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
- value.data);
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
@@ -404,8 +379,7 @@
}
if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
- value.dataType, value.data);
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
// Write the final value back to Java.
@@ -418,7 +392,7 @@
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
indices_idx++;
// out_indices must NOT be nullptr.
@@ -502,7 +476,8 @@
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
- if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+ if (out_indices != nullptr &&
+ (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
indices_idx++;
out_indices[indices_idx] = ii;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f661f29b..bab8883 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3538,7 +3538,8 @@
attrRes, bag->map.value.dataType, bag->map.value.data,
curEntry->value.dataType);
}
- if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
+ if (force || (curEntry->value.dataType == Res_value::TYPE_NULL
+ && curEntry->value.data != Res_value::DATA_NULL_EMPTY)) {
curEntry->stringBlock = bag->stringBlock;
curEntry->typeSpecFlags |= bagTypeSpecFlags;
curEntry->value = bag->map.value;
@@ -3674,7 +3675,8 @@
}
ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
return BAD_INDEX;
- } else if (type != Res_value::TYPE_NULL) {
+ } else if (type != Res_value::TYPE_NULL
+ || te.value.data == Res_value::DATA_NULL_EMPTY) {
*outValue = te.value;
return te.stringBlock;
}
@@ -5997,16 +5999,14 @@
char locale[RESTABLE_MAX_LOCALE_LEN];
forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
- if (cfg.locale != 0) {
- cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
+ cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
- const auto beginIter = locales->begin();
- const auto endIter = locales->end();
+ const auto beginIter = locales->begin();
+ const auto endIter = locales->end();
- auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
- if (iter == endIter || strcmp(iter->string(), locale) != 0) {
- locales->insertAt(String8(locale), std::distance(beginIter, iter));
- }
+ auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
+ if (iter == endIter || strcmp(iter->string(), locale) != 0) {
+ locales->insertAt(String8(locale), std::distance(beginIter, iter));
}
});
}
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index d8e5abf..fcae53b 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -264,7 +264,7 @@
const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
ASSERT_NE(nullptr, bag_two);
- ASSERT_EQ(5u, bag_two->entry_count);
+ ASSERT_EQ(6u, bag_two->entry_count);
// attr_one is inherited from StyleOne.
EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
@@ -295,6 +295,11 @@
EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
EXPECT_EQ(3u, bag_two->entries[4].value.data);
EXPECT_EQ(0, bag_two->entries[4].cookie);
+
+ EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key);
+ EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data);
+ EXPECT_EQ(0, bag_two->entries[5].cookie);
}
TEST_F(AssetManager2Test, ResolveReferenceToResource) {
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 1ff2ed4..2d73ce8 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -69,8 +69,8 @@
ResTable::Theme theme(table_);
ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
- std::array<uint32_t, 4> attrs{
- {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+ std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
@@ -109,11 +109,21 @@
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ // @empty comes from the theme, so it has the same asset cookie and changing configurations flags
+ // as the theme.
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
}
TEST_F(AttributeResolutionXmlTest, XmlParser) {
- std::array<uint32_t, 4> attrs{
- {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+ std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
@@ -121,7 +131,7 @@
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
- EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
@@ -150,16 +160,24 @@
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
}
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
ResTable::Theme theme(table_);
ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
- std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
- R::attr::attr_four, R::attr::attr_five}};
+ std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- std::array<uint32_t, attrs.size()> indices;
+ std::array<uint32_t, attrs.size() + 1> indices;
ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
attrs.size(), values.data(), indices.data());
@@ -167,12 +185,12 @@
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
uint32_t* values_cursor = values.data();
- EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
- EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
- EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
- EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
@@ -203,6 +221,20 @@
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ // @empty comes from the theme, so it has the same asset cookie and changing configurations flags
+ // as the theme.
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ // The first element of indices contains the number of indices.
+ std::array<uint32_t, 7> expected_indices = {{6u, 0u, 1u, 2u, 3u, 4u, 5u}};
+ EXPECT_EQ(expected_indices, indices);
}
} // namespace android
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
index 68527c7..05073a8 100644
--- a/libs/androidfw/tests/data/styles/R.h
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -33,6 +33,7 @@
attr_five = 0x7f010004u,
attr_indirect = 0x7f010005u,
attr_six = 0x7f010006u,
+ attr_empty = 0x7f010007u,
};
};
diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build
index 81f78b1..1ef8e6e 100755
--- a/libs/androidfw/tests/data/styles/build
+++ b/libs/androidfw/tests/data/styles/build
@@ -2,4 +2,5 @@
set -e
-aapt package -F styles.apk -M AndroidManifest.xml -S res -f
+aapt2 compile -o compiled.flata --dir res
+aapt2 link -o styles.apk --manifest AndroidManifest.xml compiled.flata
diff --git a/libs/androidfw/tests/data/styles/res/layout/layout.xml b/libs/androidfw/tests/data/styles/res/layout/layout.xml
index f3aa0f8..2c5e947 100644
--- a/libs/androidfw/tests/data/styles/res/layout/layout.xml
+++ b/libs/androidfw/tests/data/styles/res/layout/layout.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:attr_four="?attr/attr_indirect"
- app:attr_three="10" />
-
+ app:attr_three="10"
+ app:attr_one="@empty" />
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
index da592f8..3c90317 100644
--- a/libs/androidfw/tests/data/styles/res/values/styles.xml
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -33,6 +33,12 @@
<public type="attr" name="attr_indirect" id="0x7f010005" />
<attr name="attr_indirect" />
+ <public type="attr" name="attr_six" id="0x7f010006" />
+ <attr name="attr_six" />
+
+ <public type="attr" name="attr_empty" id="0x7f010007" />
+ <attr name="attr_empty" />
+
<public type="string" name="string_one" id="0x7f030000" />
<string name="string_one">Hi</string>
@@ -48,11 +54,9 @@
<item name="attr_two">"string"</item>
<item name="attr_three">?attr/attr_indirect</item>
<item name="attr_five">@string/string_one</item>
+ <item name="attr_empty">@empty</item>
</style>
-
- <public type="attr" name="attr_six" id="0x7f010006" />
- <attr name="attr_six" />
-
+
<public type="style" name="StyleThree" id="0x7f020002" />
<style name="StyleThree">
<item name="attr_six">6</item>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
index d4ccb83..72abf8f 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c157a47..a19726c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1067,7 +1067,7 @@
private void applyLevelLimits() {
int[] sampleRates = null;
Range<Integer> sampleRateRange = null, bitRates = null;
- int maxChannels = 0;
+ int maxChannels = MAX_INPUT_CHANNEL_COUNT;
String mime = mParent.getMimeType();
if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
@@ -1160,6 +1160,8 @@
if (info.containsKey("max-channel-count")) {
maxInputChannels = Utils.parseIntSafely(
info.getString("max-channel-count"), maxInputChannels);
+ } else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
+ maxInputChannels = 0;
}
if (info.containsKey("bitrate-range")) {
bitRates = bitRates.intersect(
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java
deleted file mode 100644
index 74b0c6b..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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.accessibility;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
-import android.text.Layout;
-import android.text.Spanned;
-import android.text.style.ClickableSpan;
-import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.TextView;
-
-import java.util.List;
-
-/**
- * COPIED FROM SETUP WIZARD An accessibility delegate that allows {@link
- * android.text.style.ClickableSpan} to be focused and clicked by accessibility services.
- *
- * <p>Sample usage:
- *
- * <pre>
- * LinkAccessibilityHelper mAccessibilityHelper;
- *
- * private void init() {
- * mAccessibilityHelper = new LinkAccessibilityHelper(myTextView);
- * ViewCompat.setAccessibilityDelegate(myTextView, mLinkHelper);
- * }
- *
- * {@literal @}Override
- * protected boolean dispatchHoverEvent({@literal @}NonNull MotionEvent event) {
- * if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
- * return true;
- * }
- * return super.dispatchHoverEvent(event);
- * }
- * </pre>
- *
- * @see android.support.v4.widget.ExploreByTouchHelper
- */
-public class LinkAccessibilityHelper extends ExploreByTouchHelper {
-
- private static final String TAG = "LinkAccessibilityHelper";
-
- private final TextView mView;
- private final Rect mTempRect = new Rect();
-
- public LinkAccessibilityHelper(TextView view) {
- super(view);
- mView = view;
- }
-
- @Override
- protected int getVirtualViewAt(float x, float y) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- final int offset = getOffsetForPosition(mView, x, y);
- ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (linkSpans.length == 1) {
- ClickableSpan linkSpan = linkSpans[0];
- return spannedText.getSpanStart(linkSpan);
- }
- }
- return INVALID_ID;
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- final CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- final Spanned spannedText = (Spanned) text;
- ClickableSpan[] linkSpans =
- spannedText.getSpans(0, spannedText.length(), ClickableSpan.class);
- for (ClickableSpan span : linkSpans) {
- virtualViewIds.add(spannedText.getSpanStart(span));
- }
- }
- }
-
- @Override
- protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- event.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- event.setContentDescription(mView.getText());
- }
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(
- int virtualViewId, AccessibilityNodeInfoCompat info) {
- final ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- info.setContentDescription(getTextForSpan(span));
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- info.setContentDescription(mView.getText());
- }
- info.setFocusable(true);
- info.setClickable(true);
- getBoundsForSpan(span, mTempRect);
- if (mTempRect.isEmpty()) {
- Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
- mTempRect.set(0, 0, 1, 1);
- }
- info.setBoundsInParent(mTempRect);
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- }
-
- @Override
- protected boolean onPerformActionForVirtualView(
- int virtualViewId, int action, Bundle arguments) {
- if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
- ClickableSpan span = getSpanForOffset(virtualViewId);
- if (span != null) {
- span.onClick(mView);
- return true;
- } else {
- Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
- }
- }
- return false;
- }
-
- private ClickableSpan getSpanForOffset(int offset) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
- if (spans.length == 1) {
- return spans[0];
- }
- }
- return null;
- }
-
- private CharSequence getTextForSpan(ClickableSpan span) {
- CharSequence text = mView.getText();
- if (text instanceof Spanned) {
- Spanned spannedText = (Spanned) text;
- return spannedText.subSequence(
- spannedText.getSpanStart(span), spannedText.getSpanEnd(span));
- }
- return text;
- }
-
- // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the
- // section on the first line.
- private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
- CharSequence text = mView.getText();
- outRect.setEmpty();
- if (text instanceof Spanned) {
- final Layout layout = mView.getLayout();
- if (layout != null) {
- Spanned spannedText = (Spanned) text;
- final int spanStart = spannedText.getSpanStart(span);
- final int spanEnd = spannedText.getSpanEnd(span);
- final float xStart = layout.getPrimaryHorizontal(spanStart);
- final float xEnd = layout.getPrimaryHorizontal(spanEnd);
- final int lineStart = layout.getLineForOffset(spanStart);
- final int lineEnd = layout.getLineForOffset(spanEnd);
- layout.getLineBounds(lineStart, outRect);
- if (lineEnd == lineStart) {
- // If the span is on a single line, adjust both the left and right bounds
- // so outrect is exactly bounding the span.
- outRect.left = (int) Math.min(xStart, xEnd);
- outRect.right = (int) Math.max(xStart, xEnd);
- } else {
- // If the span wraps across multiple lines, only use the first line (as returned
- // by layout.getLineBounds above), and adjust the "start" of outrect to where
- // the span starts, leaving the "end" of outrect at the end of the line.
- // ("start" being left for LTR, and right for RTL)
- if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
- outRect.right = (int) xStart;
- } else {
- outRect.left = (int) xStart;
- }
- }
-
- // Offset for padding
- outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
- }
- }
- return outRect;
- }
-
- // Compat implementation of TextView#getOffsetForPosition().
-
- private static int getOffsetForPosition(TextView view, float x, float y) {
- if (view.getLayout() == null) return -1;
- final int line = getLineAtCoordinate(view, y);
- return getOffsetAtCoordinate(view, line, x);
- }
-
- private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
- x -= view.getTotalPaddingLeft();
- // Clamp the position to inside of the view.
- x = Math.max(0.0f, x);
- x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
- x += view.getScrollX();
- return x;
- }
-
- private static int getLineAtCoordinate(TextView view, float y) {
- y -= view.getTotalPaddingTop();
- // Clamp the position to inside of the view.
- y = Math.max(0.0f, y);
- y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
- y += view.getScrollY();
- return view.getLayout().getLineForVertical((int) y);
- }
-
- private static int getOffsetAtCoordinate(TextView view, int line, float x) {
- x = convertToLocalHorizontalCoordinate(view, x);
- return view.getLayout().getOffsetForHorizontal(line, x);
- }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java b/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
index da86536..8b9315c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
@@ -16,32 +16,25 @@
package com.android.settingslib.widget;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.widget.TextView;
-import com.android.settingslib.accessibility.LinkAccessibilityHelper;
+
/**
- * Copied from setup wizard. This TextView performs two functions. The first is to make it so the
- * link behaves properly and becomes clickable. The second is that it makes the link visible to
- * accessibility services.
+ * Copied from setup wizard. This TextView performed two functions. The first is to make it so the
+ * link behaves properly and becomes clickable. The second was that it made the link visible to
+ * accessibility services, but from O forward support for links is provided natively.
*/
public class LinkTextView extends TextView {
- private LinkAccessibilityHelper mAccessibilityHelper;
-
public LinkTextView(Context context) {
this(context, null);
}
public LinkTextView(Context context, AttributeSet attrs) {
super(context, attrs);
- mAccessibilityHelper = new LinkAccessibilityHelper(this);
- ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
}
@Override
@@ -55,12 +48,4 @@
}
}
}
-
- @Override
- protected boolean dispatchHoverEvent(@NonNull MotionEvent event) {
- if (mAccessibilityHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
- }
}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f475361..9309359 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1434,6 +1434,9 @@
dumpSetting(s, p,
Settings.Secure.DEVICE_PAIRED,
SecureSettingsProto.DEVICE_PAIRED);
+ dumpSetting(s, p,
+ Settings.Secure.NOTIFICATION_BADGING,
+ SecureSettingsProto.NOTIFICATION_BADGING);
}
private static void dumpProtoSystemSettingsLocked(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 290ce1f..1d743b4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -300,7 +300,6 @@
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
- android:screenOrientation="behind"
android:resizeableActivity="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
android:theme="@style/RecentsTheme.Wallpaper">
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index bdc0871..da2d38f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -73,7 +73,8 @@
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
- mNotificationController.onActivityPinned(packageName);
+ mNotificationController.onActivityPinned(packageName,
+ true /* deferUntilAnimationEnds */);
SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
}
@@ -104,6 +105,7 @@
mTouchHandler.setTouchEnabled(true);
mTouchHandler.onPinnedStackAnimationEnded();
mMenuController.onPinnedStackAnimationEnded();
+ mNotificationController.onPinnedStackAnimationEnded();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
index 53746e2..696fdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
@@ -60,6 +60,9 @@
private PipMotionHelper mMotionHelper;
+ // Used when building a deferred notification
+ private String mDeferredNotificationPackageName;
+
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
@Override
public void onOpChanged(String op, String packageName) {
@@ -87,10 +90,47 @@
mMotionHelper = motionHelper;
}
- public void onActivityPinned(String packageName) {
+ public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
// Clear any existing notification
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ if (deferUntilAnimationEnds) {
+ mDeferredNotificationPackageName = packageName;
+ } else {
+ showNotificationForApp(mDeferredNotificationPackageName);
+ }
+
+ // Register for changes to the app ops setting for this package while it is in PiP
+ registerAppOpsListener(packageName);
+ }
+
+ public void onPinnedStackAnimationEnded() {
+ if (mDeferredNotificationPackageName != null) {
+ showNotificationForApp(mDeferredNotificationPackageName);
+ mDeferredNotificationPackageName = null;
+ }
+ }
+
+ public void onActivityUnpinned(ComponentName topPipActivity) {
+ // Unregister for changes to the previously PiP'ed package
+ unregisterAppOpsListener();
+
+ // Reset the deferred notification package
+ mDeferredNotificationPackageName = null;
+
+ if (topPipActivity != null) {
+ // onActivityUnpinned() is only called after the transition is complete, so we don't
+ // need to defer until the animation ends to update the notification
+ onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
+ } else {
+ mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ }
+ }
+
+ /**
+ * Builds and shows the notification for the given app.
+ */
+ private void showNotificationForApp(String packageName) {
// Build a new notification
final Notification.Builder builder =
new Notification.Builder(mContext, NotificationChannels.GENERAL)
@@ -105,20 +145,6 @@
// Show the new notification
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
}
-
- // Register for changes to the app ops setting for this package while it is in PiP
- registerAppOpsListener(packageName);
- }
-
- public void onActivityUnpinned(ComponentName topPipActivity) {
- // Unregister for changes to the previously PiP'ed package
- unregisterAppOpsListener();
-
- if (topPipActivity != null) {
- onActivityPinned(topPipActivity.getPackageName());
- } else {
- mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 90c65580..8f24ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -767,17 +767,22 @@
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mIsInMinimizeInteraction = true;
- if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())) {
+ if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())
+ && (mDividerPositionBeforeMinimized <= 0 || !mAdjustedForIme)) {
mDividerPositionBeforeMinimized = getCurrentPosition();
}
mMinimizedSnapAlgorithm = null;
mDockedStackMinimized = minimized;
initializeSnapAlgorithm();
- stopDragging(getCurrentPosition(), minimized ?
- mMinimizedSnapAlgorithm.getMiddleTarget() :
- mSnapAlgorithm.calculateNonDismissingSnapTarget(
+ stopDragging(minimized
+ ? mDividerPositionBeforeMinimized
+ : getCurrentPosition(),
+ minimized
+ ? mMinimizedSnapAlgorithm.getMiddleTarget()
+ : mSnapAlgorithm.calculateNonDismissingSnapTarget(
mDividerPositionBeforeMinimized),
animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
+ setAdjustedForIme(false, animDuration);
}
if (!minimized) {
mBackground.animate().withEndAction(mResetBackgroundRunnable);
@@ -820,6 +825,9 @@
.setDuration(animDuration)
.start();
mAdjustedForIme = adjustedForIme;
+ if (mHomeStackResizable && adjustedForIme) {
+ mDividerPositionBeforeMinimized = getCurrentPosition();
+ }
}
private void resetBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 30ff30f..be221bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -341,14 +341,12 @@
public void appTransitionPending(boolean forced) {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
}
}
public void appTransitionCancelled() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_CANCELLED);
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
}
}
@@ -359,7 +357,6 @@
public void appTransitionStarting(long startTime, long duration, boolean forced) {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_STARTING);
mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
Pair.create(startTime, duration)).sendToTarget();
}
@@ -368,7 +365,6 @@
@Override
public void appTransitionFinished() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c5853ca..6a9bfae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2499,7 +2499,7 @@
if (animate) {
mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
mDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
mDarkAnimator.start();
} else {
setDarkAmount(darkAmount);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b2712ff..aad4431 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -4365,7 +4365,7 @@
}
addWidgetLocked(id);
}
- if (id.provider.info != null) {
+ if (id.provider != null && id.provider.info != null) {
stashProviderRestoreUpdateLocked(id.provider,
restoredId, id.appWidgetId);
} else {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 0999580..41a78a7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -20,6 +20,7 @@
import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.bundleToString;
@@ -397,6 +398,21 @@
}
}
+ // Called by Shell command.
+ public int getMaxPartitions() {
+ synchronized (mLock) {
+ return sPartitionMaxCount;
+ }
+ }
+
+ // Called by Shell command.
+ public void setMaxPartitions(int max) {
+ Slog.i(TAG, "setMaxPartitions(): " + max);
+ synchronized (mLock) {
+ sPartitionMaxCount = max;
+ }
+ }
+
private void setDebugLocked(boolean debug) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
@@ -628,6 +644,7 @@
pw.print("Debug mode: "); pw.println(oldDebug);
pw.print("Verbose mode: "); pw.println(sVerbose);
pw.print("Disabled users: "); pw.println(mDisabledUsers);
+ pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
final int size = mServicesCache.size();
pw.print("Cached services: ");
if (size == 0) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 1b9c86e..f3de557 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -70,9 +70,15 @@
pw.println(" get log_level ");
pw.println(" Gets the Autofill log level (off | debug | verbose).");
pw.println("");
+ pw.println(" get max_partitions");
+ pw.println(" Gets the maximum number of partitions per session.");
+ pw.println("");
pw.println(" set log_level [off | debug | verbose]");
pw.println(" Sets the Autofill log level.");
pw.println("");
+ pw.println(" set max_partitions number");
+ pw.println(" Sets the maximum number of partitions per session.");
+ pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" List all pending sessions.");
pw.println("");
@@ -86,9 +92,33 @@
}
private int requestGet(PrintWriter pw) {
- if (!isNextArgLogLevel(pw, "get")) {
- return -1;
+ final String what = getNextArgRequired();
+ switch(what) {
+ case "log_level":
+ return getLogLevel(pw);
+ case "max_partitions":
+ return getMaxPartitions(pw);
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
}
+ }
+
+ private int requestSet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+
+ switch(what) {
+ case "log_level":
+ return setLogLevel(pw);
+ case "max_partitions":
+ return setMaxPartitions();
+ default:
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+ }
+
+ private int getLogLevel(PrintWriter pw) {
final int logLevel = mService.getLogLevel();
switch (logLevel) {
case AutofillManager.FLAG_ADD_CLIENT_VERBOSE:
@@ -106,11 +136,8 @@
}
}
- private int requestSet(PrintWriter pw) {
- if (!isNextArgLogLevel(pw, "set")) {
- return -1;
- }
- final String logLevel = getNextArg();
+ private int setLogLevel(PrintWriter pw) {
+ final String logLevel = getNextArgRequired();
switch (logLevel.toLowerCase()) {
case "verbose":
mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_VERBOSE);
@@ -127,6 +154,16 @@
}
}
+ private int getMaxPartitions(PrintWriter pw) {
+ pw.println(mService.getMaxPartitions());
+ return 0;
+ }
+
+ private int setMaxPartitions() {
+ mService.setMaxPartitions(Integer.parseInt(getNextArgRequired()));
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index ffcde8d..0281f73 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -26,16 +26,23 @@
/**
* Defines a logging flag that can be dynamically changed at runtime using
- * {@code cmd autofill debug [on|off]}.
+ * {@code cmd autofill set log_level debug}.
*/
public static boolean sDebug = false;
/**
* Defines a logging flag that can be dynamically changed at runtime using
- * {@code cmd autofill verbose [on|off]}.
+ * {@code cmd autofill set log_level verbose}.
*/
public static boolean sVerbose = false;
+ /**
+ * Maximum number of partitions that can be allowed in a session.
+ *
+ * <p>Can be modified using {@code cmd autofill set max_partitions}.
+ */
+ static int sPartitionMaxCount = 10;
+
private Helper() {
throw new UnsupportedOperationException("contains static members only");
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index a12ebb2..35f4fae 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -27,11 +27,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.ICancellationSignal;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.autofill.AutofillService;
import android.service.autofill.FillRequest;
@@ -43,6 +45,7 @@
import android.text.format.DateUtils;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.HandlerCaller;
import com.android.server.FgThread;
@@ -63,6 +66,9 @@
// How long after the last interaction with the service we would unbind
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+ // How long after we make a remote request to a fill service we timeout
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+
private final Context mContext;
private final ComponentName mComponentName;
@@ -413,12 +419,18 @@
private static final class PendingFillRequest extends PendingRequest {
private final Object mLock = new Object();
+
private final WeakReference<RemoteFillService> mWeakService;
private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
+
+ @GuardedBy("mLock")
private boolean mCancelled;
+ @GuardedBy("mLock")
+ private boolean mCompleted;
+
public PendingFillRequest(FillRequest request, RemoteFillService service) {
mRequest = request;
mWeakService = new WeakReference<>(service);
@@ -443,8 +455,15 @@
@Override
public void onSuccess(FillResponse response) {
+ synchronized (mLock) {
+ if (mCompleted) {
+ return;
+ }
+ mCompleted = true;
+ }
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
+ service.mHandler.getHandler().removeCallbacks(PendingFillRequest.this);
remoteService.dispatchOnFillRequestSuccess(PendingFillRequest.this,
getCallingUid(), request.getFlags(), response);
}
@@ -452,13 +471,29 @@
@Override
public void onFailure(CharSequence message) {
+ synchronized (mLock) {
+ if (mCompleted) {
+ return;
+ }
+ mCompleted = true;
+ }
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
+ service.mHandler.getHandler().removeCallbacks(PendingFillRequest.this);
remoteService.dispatchOnFillRequestFailure(
PendingFillRequest.this, message);
}
}
};
+ service.mHandler.getHandler().postAtTime(() -> {
+ cancel();
+ try {
+ mCallback.onFailure(null);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }, PendingFillRequest.this,
+ SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
}
@Override
@@ -496,10 +531,15 @@
}
private static final class PendingSaveRequest extends PendingRequest {
+ private final Object mLock = new Object();
+
private final WeakReference<RemoteFillService> mWeakService;
private final SaveRequest mRequest;
private final ISaveCallback mCallback;
+ @GuardedBy("mLock")
+ private boolean mCompleted;
+
public PendingSaveRequest(@NonNull SaveRequest request,
@NonNull RemoteFillService service) {
mRequest = request;
@@ -507,8 +547,16 @@
mCallback = new ISaveCallback.Stub() {
@Override
public void onSuccess() {
+ synchronized (mLock) {
+ if (mCompleted) {
+ return;
+ }
+ mCompleted = true;
+ }
+ cancel();
RemoteFillService service = mWeakService.get();
if (service != null) {
+ service.mHandler.getHandler().removeCallbacks(PendingSaveRequest.this);
service.dispatchOnSaveRequestSuccess(
PendingSaveRequest.this);
}
@@ -516,13 +564,29 @@
@Override
public void onFailure(CharSequence message) {
+ synchronized (mLock) {
+ if (mCompleted) {
+ return;
+ }
+ mCompleted = true;
+ }
RemoteFillService service = mWeakService.get();
if (service != null) {
+ service.mHandler.getHandler().removeCallbacks(PendingSaveRequest.this);
service.dispatchOnSaveRequestFailure(
PendingSaveRequest.this, message);
}
}
};
+ service.mHandler.getHandler().postAtTime(() -> {
+ cancel();
+ try {
+ mCallback.onFailure(null);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }, PendingSaveRequest.this,
+ SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ef5cdd1..4746ee9 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -162,6 +163,7 @@
@GuardedBy("mLock")
private boolean mIsSaving;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -933,7 +935,6 @@
}
}
- private static final int PARTITION_MAX_COUNT = 64;
/**
* Determines if a new partition should be started for an id.
*
@@ -947,8 +948,9 @@
}
final int numResponses = mResponses.size();
- if (numResponses >= PARTITION_MAX_COUNT) {
- Slog.e(TAG, "Cannot create more than 64 partitions. Not creating a new partition.");
+ if (numResponses >= sPartitionMaxCount) {
+ Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
+ + " reached maximum of " + sPartitionMaxCount);
return false;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1ed46a0..5e03508 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1493,12 +1493,13 @@
mAm.updateOomAdjLocked(r.binding.service.app, false);
}
}
+
+ mAm.updateOomAdjLocked();
+
} finally {
Binder.restoreCallingIdentity(origId);
}
- mAm.updateOomAdjLocked();
-
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7304c22..5283924 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1472,6 +1472,20 @@
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
boolean mForceResizableActivities;
+ /**
+ * Flag that indicates if multi-window is enabled.
+ *
+ * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+ * in {@link com.android.internal.R.bool.config_supportsMultiWindow} config or
+ * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+ * At least one of the forms of multi-window must be enabled in order for this flag to be
+ * initialized to 'true'.
+ *
+ * @see #mSupportsSplitScreenMultiWindow
+ * @see #mSupportsFreeformWindowManagement
+ * @see #mSupportsPictureInPicture
+ * @see #mSupportsMultiDisplay
+ */
boolean mSupportsMultiWindow;
boolean mSupportsSplitScreenMultiWindow;
boolean mSupportsFreeformWindowManagement;
@@ -4598,7 +4612,7 @@
mAppSwitchesAllowedTime = 0;
}
}
- int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null,
+ int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions, null);
return ret;
}
@@ -7504,11 +7518,12 @@
}
@Override
- public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+ public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
+ Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
if (target instanceof PendingIntentRecord) {
return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
- finishedReceiver, requiredPermission, options);
+ whitelistToken, finishedReceiver, requiredPermission, options);
} else {
if (intent == null) {
// Weird case: someone has given us their own custom IIntentSender, and now
@@ -7520,7 +7535,8 @@
intent = new Intent(Intent.ACTION_MAIN);
}
try {
- target.send(code, intent, resolvedType, null, requiredPermission, options);
+ target.send(code, intent, resolvedType, whitelistToken, null,
+ requiredPermission, options);
} catch (RemoteException e) {
}
// Platform code can rely on getting a result back when the send is done, but if
@@ -7817,11 +7833,15 @@
// be guarded by permission checking.
int getUidState(int uid) {
synchronized (this) {
- UidRecord uidRec = mActiveUids.get(uid);
- return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+ return getUidStateLocked(uid);
}
}
+ int getUidStateLocked(int uid) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+ }
+
@Override
public boolean isInMultiWindowMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
@@ -13387,8 +13407,12 @@
public void setRenderThread(int tid) {
synchronized (this) {
ProcessRecord proc;
+ int pid = Binder.getCallingPid();
+ if (pid == Process.myPid()) {
+ demoteSystemServerRenderThread(tid);
+ return;
+ }
synchronized (mPidsSelfLocked) {
- int pid = Binder.getCallingPid();
proc = mPidsSelfLocked.get(pid);
if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
// ensure the tid belongs to the process
@@ -13421,6 +13445,16 @@
}
}
+ /**
+ * We only use RenderThread in system_server to store task snapshots to the disk, which should
+ * happen in the background. Thus, demote render thread from system_server to a lower priority.
+ *
+ * @param tid the tid of the RenderThread
+ */
+ private void demoteSystemServerRenderThread(int tid) {
+ setThreadPriority(tid, Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
@@ -13926,16 +13960,23 @@
mAlwaysFinishActivities = alwaysFinishActivities;
mSupportsLeanbackOnly = supportsLeanbackOnly;
mForceResizableActivities = forceResizable;
- if (supportsMultiWindow || forceResizable) {
+ final boolean multiWindowFormEnabled = freeformWindowManagement
+ || supportsSplitScreenMultiWindow
+ || supportsPictureInPicture
+ || supportsMultiDisplay;
+ if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
mSupportsMultiWindow = true;
- mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
+ mSupportsFreeformWindowManagement = freeformWindowManagement;
+ mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+ mSupportsPictureInPicture = supportsPictureInPicture;
+ mSupportsMultiDisplay = supportsMultiDisplay;
} else {
mSupportsMultiWindow = false;
mSupportsFreeformWindowManagement = false;
+ mSupportsSplitScreenMultiWindow = false;
+ mSupportsPictureInPicture = false;
+ mSupportsMultiDisplay = false;
}
- mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
- mSupportsPictureInPicture = supportsPictureInPicture;
- mSupportsMultiDisplay = supportsMultiDisplay;
mWindowManager.setForceResizableTasks(mForceResizableActivities);
mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
// This happens before any activities are started, so we can change global configuration
@@ -23642,12 +23683,13 @@
}
@Override
- public void setPendingIntentWhitelistDuration(IIntentSender target, long duration) {
+ public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
+ long duration) {
if (!(target instanceof PendingIntentRecord)) {
Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
return;
}
- ((PendingIntentRecord) target).setWhitelistDurationLocked(duration);
+ ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index dab122f..6eae9e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -22,11 +22,8 @@
import android.app.IActivityContainer;
import android.app.IActivityController;
import android.app.IActivityManager;
-import android.app.IInstrumentationWatcher;
import android.app.IStopUserCallback;
-import android.app.Instrumentation;
import android.app.ProfilerInfo;
-import android.app.UiAutomationConnection;
import android.app.WaitResult;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
@@ -37,7 +34,6 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5c57be2..f13dad7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -2180,7 +2180,7 @@
void setRequestedOrientation(int requestedOrientation) {
if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen
- && appInfo.targetSdkVersion >= O) {
+ && appInfo.targetSdkVersion > O) {
throw new IllegalStateException("Only fullscreen activities can request orientation");
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8b7efff..4049500 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4731,7 +4731,7 @@
checkEmbeddedAllowedInner(userId, pendingIntent.key.requestIntent,
pendingIntent.key.requestResolvedType);
- return pendingIntent.sendInner(0, null, null, null, null, null, null, 0,
+ return pendingIntent.sendInner(0, null, null, null, null, null, null, null, 0,
FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 7ba67c5..6eca3fa 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -32,6 +32,7 @@
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
@@ -51,7 +52,7 @@
final WeakReference<PendingIntentRecord> ref;
boolean sent = false;
boolean canceled = false;
- private long whitelistDuration = 0;
+ private ArrayMap<IBinder, Long> whitelistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
String stringName;
@@ -194,8 +195,19 @@
ref = new WeakReference<PendingIntentRecord>(this);
}
- void setWhitelistDurationLocked(long duration) {
- this.whitelistDuration = duration;
+ void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
+ if (duration > 0) {
+ if (whitelistDuration == null) {
+ whitelistDuration = new ArrayMap<>();
+ }
+ whitelistDuration.put(whitelistToken, duration);
+ } else if (whitelistDuration != null) {
+ whitelistDuration.remove(whitelistToken);
+ if (whitelistDuration.size() <= 0) {
+ whitelistDuration = null;
+ }
+
+ }
this.stringName = null;
}
@@ -219,19 +231,20 @@
return listeners;
}
- public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- sendInner(code, intent, resolvedType, finishedReceiver,
- requiredPermission, null, null, 0, 0, 0, options, null);
- }
-
- public int sendWithResult(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- return sendInner(code, intent, resolvedType, finishedReceiver,
+ sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
requiredPermission, null, null, 0, 0, 0, options, null);
}
- int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
+ public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
+ requiredPermission, null, null, 0, 0, 0, options, null);
+ }
+
+ int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver,
String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
if (intent != null) intent.setDefusable(true);
@@ -276,20 +289,29 @@
final long origId = Binder.clearCallingIdentity();
- if (whitelistDuration > 0) {
- StringBuilder tag = new StringBuilder(64);
- tag.append("pendingintent:");
- UserHandle.formatUid(tag, callingUid);
- tag.append(":");
- if (finalIntent.getAction() != null) {
- tag.append(finalIntent.getAction());
- } else if (finalIntent.getComponent() != null) {
- finalIntent.getComponent().appendShortString(tag);
- } else if (finalIntent.getData() != null) {
- tag.append(finalIntent.getData());
+ if (whitelistDuration != null) {
+ Long duration = whitelistDuration.get(whitelistToken);
+ if (duration != null) {
+ int procState = owner.getUidState(callingUid);
+ if (!ActivityManager.isProcStateBackground(procState)) {
+ StringBuilder tag = new StringBuilder(64);
+ tag.append("pendingintent:");
+ UserHandle.formatUid(tag, callingUid);
+ tag.append(":");
+ if (finalIntent.getAction() != null) {
+ tag.append(finalIntent.getAction());
+ } else if (finalIntent.getComponent() != null) {
+ finalIntent.getComponent().appendShortString(tag);
+ } else if (finalIntent.getData() != null) {
+ tag.append(finalIntent.getData());
+ }
+ owner.tempWhitelistForPendingIntentLocked(callingPid,
+ callingUid, uid, duration, tag.toString());
+ } else {
+ Slog.w(TAG, "Not doing whitelist " + this + ": caller state="
+ + procState);
+ }
}
- owner.tempWhitelistForPendingIntentLocked(callingPid,
- callingUid, uid, whitelistDuration, tag.toString());
}
boolean sendFinish = finishedReceiver != null;
@@ -425,10 +447,17 @@
pw.print(prefix); pw.print("sent="); pw.print(sent);
pw.print(" canceled="); pw.println(canceled);
}
- if (whitelistDuration != 0) {
+ if (whitelistDuration != null) {
pw.print(prefix);
pw.print("whitelistDuration=");
- TimeUtils.formatDuration(whitelistDuration, pw);
+ for (int i = 0; i < whitelistDuration.size(); i++) {
+ if (i != 0) {
+ pw.print(", ");
+ }
+ pw.print(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ pw.print(":");
+ TimeUtils.formatDuration(whitelistDuration.valueAt(i), pw);
+ }
pw.println();
}
if (mCancelCallbacks != null) {
@@ -451,9 +480,16 @@
sb.append(key.packageName);
sb.append(' ');
sb.append(key.typeName());
- if (whitelistDuration > 0) {
+ if (whitelistDuration != null) {
sb.append( " (whitelist: ");
- TimeUtils.formatDuration(whitelistDuration, sb);
+ for (int i = 0; i < whitelistDuration.size(); i++) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ sb.append(":");
+ TimeUtils.formatDuration(whitelistDuration.valueAt(i), sb);
+ }
sb.append(")");
}
sb.append('}');
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index e6edaf1..1bd2085 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -41,9 +41,10 @@
if (DBG) Slog.d(TAG, "missing config");
return null;
}
+ boolean userWantsBadges = mConfig.badgingEnabled(record.sbn.getUser());
boolean appCanShowBadge =
mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
- if (!appCanShowBadge) {
+ if (!userWantsBadges || !appCanShowBadge) {
record.setShowBadge(false);
} else {
record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 343aab1..9cd0dff 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -344,6 +344,7 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
+ private static final IBinder WHITELIST_TOKEN = new Binder();
private RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -926,6 +927,8 @@
};
private final class SettingsObserver extends ContentObserver {
+ private final Uri NOTIFICATION_BADGING_URI
+ = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_RATE_LIMIT_URI
@@ -937,6 +940,8 @@
void observe() {
ContentResolver resolver = getContext().getContentResolver();
+ resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
+ false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
@@ -962,6 +967,9 @@
mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
}
+ if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
+ mRankingHelper.updateBadgingEnabled();
+ }
}
}
@@ -983,6 +991,7 @@
public NotificationManagerService(Context context) {
super(context);
+ Notification.processWhitelistToken = WHITELIST_TOKEN;
}
// TODO - replace these methods with a single VisibleForTesting constructor
@@ -3262,7 +3271,8 @@
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
- am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
+ am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
+ WHITELIST_TOKEN, duration);
}
}
}
@@ -4025,6 +4035,7 @@
private void handleRankingSort(Message msg) {
if (!(msg.obj instanceof Boolean)) return;
+ if (mRankingHelper == null) return;
boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
synchronized (mNotificationLock) {
final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 4d19b52..36da04d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -18,6 +18,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
import java.util.Collection;
@@ -27,6 +28,7 @@
int getImportance(String packageName, int uid);
void setShowBadge(String packageName, int uid, boolean showBadge);
boolean canShowBadge(String packageName, int uid);
+ boolean badgingEnabled(UserHandle userHandle);
Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index d4b6461..e83d453 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -21,6 +21,8 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.Preconditions;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -33,10 +35,14 @@
import android.metrics.LogMaker;
import android.os.Build;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import org.json.JSONArray;
import org.json.JSONException;
@@ -48,11 +54,13 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
public class RankingHelper implements RankingConfig {
private static final String TAG = "RankingHelper";
@@ -89,6 +97,7 @@
private final Context mContext;
private final RankingHandler mRankingHandler;
private final PackageManager mPm;
+ private SparseBooleanArray mBadgingEnabled;
public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
NotificationUsageStats usageStats, String[] extractorNames) {
@@ -98,6 +107,8 @@
mPreliminaryComparator = new NotificationComparator(mContext);
+ updateBadgingEnabled();
+
final int N = extractorNames.length;
mSignalExtractors = new NotificationSignalExtractor[N];
for (int i = 0; i < N; i++) {
@@ -575,12 +586,8 @@
updateConfig();
}
- private void clearLockedFields(NotificationChannel channel) {
- int clearMask = 0;
- for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
- clearMask |= NotificationChannel.LOCKABLE_FIELDS[i];
- }
- channel.lockFields(~clearMask);
+ void clearLockedFields(NotificationChannel channel) {
+ channel.unlockFields(channel.getUserLockedFields());
}
@Override
@@ -598,6 +605,7 @@
if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
+ lockFieldsForUpdate(channel, updatedChannel);
r.channels.put(updatedChannel.getId(), updatedChannel);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
@@ -821,6 +829,35 @@
enabled ? DEFAULT_IMPORTANCE : NotificationManager.IMPORTANCE_NONE);
}
+ @VisibleForTesting
+ void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
+ update.unlockFields(update.getUserLockedFields());
+ update.lockFields(original.getUserLockedFields());
+ if (original.canBypassDnd() != update.canBypassDnd()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ }
+ if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+ }
+ if (original.getImportance() != update.getImportance()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ }
+ if (original.shouldShowLights() != update.shouldShowLights()
+ || original.getLightColor() != update.getLightColor()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
+ }
+ if (!Objects.equals(original.getSound(), update.getSound())) {
+ update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+ }
+ if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+ || original.shouldVibrate() != update.shouldVibrate()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
+ }
+ if (original.canShowBadge() != update.canShowBadge()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
+ }
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
if (filter == null) {
final int N = mSignalExtractors.length;
@@ -1099,6 +1136,38 @@
channel.getImportance());
}
+ public void updateBadgingEnabled() {
+ if (mBadgingEnabled == null) {
+ mBadgingEnabled = new SparseBooleanArray();
+ }
+ boolean changed = false;
+ // update the cached values
+ for (int index = 0; index < mBadgingEnabled.size(); index++) {
+ int userId = mBadgingEnabled.keyAt(index);
+ final boolean oldValue = mBadgingEnabled.get(userId);
+ final boolean newValue = Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
+ mBadgingEnabled.put(userId, newValue);
+ changed |= oldValue != newValue;
+ }
+ if (changed) {
+ mRankingHandler.requestSort(false);
+ }
+ }
+
+ public boolean badgingEnabled(UserHandle userHandle) {
+ int userId = userHandle.getIdentifier();
+ if (mBadgingEnabled.indexOfKey(userId) < 0) {
+ mBadgingEnabled.put(userId,
+ Secure.getIntForUser(mContext.getContentResolver(),
+ Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
+ }
+ return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
+ }
+
+
private static class Record {
static int UNKNOWN_UID = UserHandle.USER_NULL;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index bbd4048..575e0f9 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -21,6 +21,7 @@
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Locale;
class OtaDexoptShellCommand extends ShellCommand {
final IOtaDexopt mInterface;
@@ -93,7 +94,10 @@
private int runOtaProgress() throws RemoteException {
final float progress = mInterface.getProgress();
final PrintWriter pw = getOutPrintWriter();
- pw.format("%.2f", progress);
+ // Note: The float output is parsed by update_engine. It does needs to be non-localized,
+ // as it's always expected to be "0.xy," never "0,xy" or similar. So use the ROOT
+ // Locale for formatting. (b/37760573)
+ pw.format(Locale.ROOT, "%.2f", progress);
return 0;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4b5241..093cdbc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3615,7 +3615,7 @@
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
@@ -3630,14 +3630,14 @@
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
- Binder.getCallingUid(), userId)) {
+ Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
@@ -3646,13 +3646,17 @@
return null;
}
-
- private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId) {
- // System/shell/root get to see all static libs
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
- || appId == Process.ROOT_UID) {
- return false;
+ private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+ int flags) {
+ // Callers can access only the libs they depend on, otherwise they need to explicitly
+ // ask for the shared libraries given the caller is allowed to access all static libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+ // System/shell/root get to see all static libs
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+ || appId == Process.ROOT_UID) {
+ return false;
+ }
}
// No package means no static lib as it is always on internal storage
@@ -3847,7 +3851,7 @@
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- if (filterSharedLibPackageLPr(ps, uid, userId)) {
+ if (filterSharedLibPackageLPr(ps, uid, userId, flags)) {
return null;
}
if (ps.pkg == null) {
@@ -3888,7 +3892,7 @@
if (p != null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
return null;
}
// Note: isEnabledLP() does not apply here - always return info
@@ -4349,7 +4353,6 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- // TODO: We will change version code to long, so in the new API it is long
PackageInfo packageInfo = getPackageInfoVersioned(
libInfo.getDeclaringPackage(), flags, userId);
if (packageInfo == null) {
@@ -4492,7 +4495,8 @@
}
PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
- UserHandle.getUserId(Binder.getCallingUid()))) {
+ UserHandle.getUserId(Binder.getCallingUid()),
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
@@ -7547,7 +7551,7 @@
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
@@ -7559,7 +7563,7 @@
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
- Binder.getCallingUid(), userId)) {
+ Binder.getCallingUid(), userId, flags)) {
continue;
}
final PackageInfo pi = generatePackageInfo((PackageSetting)
@@ -7664,7 +7668,7 @@
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
if (ps.pkg != null) {
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
@@ -7688,7 +7692,7 @@
for (PackageParser.Package p : mPackages.values()) {
if (p.mExtras != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
- if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+ if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c1d68b8..3e920d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -48,6 +48,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.SystemProperties;
@@ -1686,7 +1687,7 @@
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6aff600..8e058ad 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5325,7 +5325,7 @@
@Override
public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
WindowState attached, WindowState imeTarget) {
- final boolean visible = !win.isGoneForLayoutLw() && win.getAttrs().alpha > 0f;
+ final boolean visible = win.isVisibleLw() && win.getAttrs().alpha > 0f;
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible);
applyKeyguardPolicyLw(win, imeTarget);
final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -5960,15 +5960,23 @@
result &= ~ACTION_PASS_TO_USER;
break;
}
- if (telecomManager.isInCall()
- && (result & ACTION_PASS_TO_USER) == 0) {
- // If we are in call but we decided not to pass the key to
- // the application, just pass it to the session service.
- MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
- event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
- break;
- }
}
+ int audioMode = AudioManager.MODE_NORMAL;
+ try {
+ audioMode = getAudioService().getMode();
+ } catch (Exception e) {
+ Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
+ }
+ boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
+ audioMode == AudioManager.MODE_IN_COMMUNICATION;
+ if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
+ // If we are in call but we decided not to pass the key to
+ // the application, just pass it to the session service.
+ MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+ event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
+ break;
+ }
+
}
if (mUseTvRouting || mHandleVolumeKeysInWM) {
// Defer special key handlings to
@@ -6313,7 +6321,7 @@
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
}
break;
@@ -6321,7 +6329,7 @@
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
}
break;
@@ -6332,7 +6340,7 @@
AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
}
- } catch (RemoteException e) {
+ } catch (Exception e) {
Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
}
break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 423bc0c..a8d19e9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4607,6 +4607,9 @@
} catch (IOException e) {
Slog.e(TAG, "Failed to read last_reboot_reason file", e);
}
+ if (line == null) {
+ return PowerManager.SHUTDOWN_REASON_UNKNOWN;
+ }
switch (line) {
case REASON_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_SHUTDOWN;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6a18beb..39b902e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -59,6 +59,7 @@
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Process;
@@ -663,13 +664,35 @@
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
+ Slog.w(TAG, "Wallpaper service gone: " + name);
+ if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
+ Slog.e(TAG, "Does not match expected wallpaper component "
+ + mWallpaper.wallpaperComponent);
+ }
mService = null;
mEngine = null;
if (mWallpaper.connection == this) {
- // The wallpaper disappeared. If this isn't a system-default one, track
- // crashes and fall back to default if it continues to misbehave.
+ // There is an inherent ordering race between this callback and the
+ // package monitor that receives notice that a package is being updated,
+ // so we cannot quite trust at this moment that we know for sure that
+ // this is not an update. If we think this is a genuine non-update
+ // wallpaper outage, we do our "wait for reset" work as a continuation,
+ // a short time in the future, specifically to allow any pending package
+ // update message on this same looper thread to be processed.
+ if (!mWallpaper.wallpaperUpdating) {
+ mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+ 1000);
+ }
+ }
+ }
+ }
+
+ private void processDisconnect(final ServiceConnection connection) {
+ synchronized (mLock) {
+ // The wallpaper disappeared. If this isn't a system-default one, track
+ // crashes and fall back to default if it continues to misbehave.
+ if (connection == mWallpaper.connection) {
final ComponentName wpService = mWallpaper.wallpaperComponent;
- Slog.w(TAG, "Wallpaper service gone: " + wpService);
if (!mWallpaper.wallpaperUpdating
&& mWallpaper.userId == mCurrentUserId
&& !Objects.equals(mDefaultWallpaperComponent, wpService)
@@ -682,7 +705,7 @@
// during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
if (mWallpaper.lastDiedTime != 0
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
- > SystemClock.uptimeMillis()) {
+ > SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
} else {
@@ -690,18 +713,22 @@
// If we didn't reset it right away, do so after we couldn't connect to
// it for an extended amount of time to avoid having a black wallpaper.
- FgThread.getHandler().removeCallbacks(mResetRunnable);
- FgThread.getHandler().postDelayed(mResetRunnable,
- WALLPAPER_RECONNECT_TIMEOUT_MS);
+ final Handler fgHandler = FgThread.getHandler();
+ fgHandler.removeCallbacks(mResetRunnable);
+ fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
if (DEBUG_LIVE) {
Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
}
}
- final String flattened = name.flattenToString();
+ final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
}
+ } else {
+ if (DEBUG_LIVE) {
+ Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 982561c..96ea5e5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1248,11 +1248,11 @@
*/
@Override
int getOrientation(int candidate) {
- // We do not allow non-fullscreen apps to influence orientation at and beyond O. While we do
+ // We do not allow non-fullscreen apps to influence orientation beyond O. While we do
// throw an exception in {@link Activity#onCreate} and
// {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that
// other calculations aren't affected.
- if (!fillsParent() && mTargetSdk >= O) {
+ if (!fillsParent() && mTargetSdk > O) {
// Can't specify orientation if app doesn't fill parent.
return SCREEN_ORIENTATION_UNSET;
}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index b927e67..9c44c14 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import android.app.ActivityManager.StackId;
import android.app.RemoteAction;
@@ -285,13 +286,16 @@
if (StackId.tasksAreFloating(mStackId)) {
// Floating tasks should not be resized to the screen's bounds.
- if (bounds.width() == mTmpDisplayBounds.width() &&
+ if (mStackId == PINNED_STACK_ID && bounds.width() == mTmpDisplayBounds.width() &&
bounds.height() == mTmpDisplayBounds.height()) {
// If the bounds we are animating is the same as the fullscreen stack
// dimensions, then apply the same inset calculations that we normally do for
// the fullscreen stack, without intersecting it with the display bounds
stableBounds.inset(mTmpStableInsets);
nonDecorBounds.inset(mTmpNonDecorInsets);
+ // Move app bounds to zero to apply intersection with parent correctly. They are
+ // used only for evaluating width and height, so it's OK to move them around.
+ config.appBounds.offsetTo(0, 0);
intersectParentBounds = true;
}
width = (int) (stableBounds.width() / density);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b79173c..1bbe1d0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -275,6 +275,15 @@
mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
}
+ /**
+ * Temporarily pauses/unpauses persisting of task snapshots.
+ *
+ * @param paused Whether task snapshot persisting should be paused.
+ */
+ void setPersisterPaused(boolean paused) {
+ mPersister.setPaused(paused);
+ }
+
void dump(PrintWriter pw, String prefix) {
mCache.dump(pw, prefix);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index e5c7a72..0287070 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -60,6 +60,8 @@
private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>();
@GuardedBy("mLock")
private boolean mQueueIdling;
+ @GuardedBy("mLock")
+ private boolean mPaused;
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
@@ -127,6 +129,15 @@
}
}
+ void setPaused(boolean paused) {
+ synchronized (mLock) {
+ mPaused = paused;
+ if (!paused) {
+ mLock.notifyAll();
+ }
+ }
+ }
+
@TestApi
void waitForQueueEmpty() {
while (true) {
@@ -142,7 +153,9 @@
@GuardedBy("mLock")
private void sendToQueueLocked(WriteQueueItem item) {
mWriteQueue.offer(item);
- mLock.notifyAll();
+ if (!mPaused) {
+ mLock.notifyAll();
+ }
}
private File getDirectory(int userId) {
@@ -185,7 +198,11 @@
while (true) {
WriteQueueItem next;
synchronized (mLock) {
- next = mWriteQueue.poll();
+ if (mPaused) {
+ next = null;
+ } else {
+ next = mWriteQueue.poll();
+ }
}
if (next != null) {
next.write();
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 067cc09..dd790a9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -255,12 +255,19 @@
mWindowPlacerLocked.requestTraversal();
}
- if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ if (mAnimating && !wasAnimating) {
+
+ // Usually app transitions but quite a load onto the system already (with all the things
+ // happening in app), so pause task snapshot persisting to not increase the load.
+ mService.mTaskSnapshotController.setPersisterPaused(true);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
}
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
+ mService.mTaskSnapshotController.setPersisterPaused(false);
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
index 0cf4994..262516d 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -18,20 +18,19 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
import android.app.ActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.content.Context;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -43,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BadgeExtractorTest {
+public class BadgeExtractorTest extends NotificationTestCase {
@Mock RankingConfig mConfig;
@@ -59,7 +58,11 @@
MockitoAnnotations.initMocks(this);
}
- private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+ private NotificationRecord getNotificationRecord(boolean showBadge, int importanceHigh) {
+ NotificationChannel channel = new NotificationChannel("a", "a", importanceHigh);
+ channel.setShowBadge(showBadge);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -73,10 +76,6 @@
return r;
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
@@ -86,13 +85,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
- channel.setShowBadge(false);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
extractor.process(r);
@@ -104,13 +99,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
- channel.setShowBadge(true);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
extractor.process(r);
@@ -122,13 +113,9 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- channel.setShowBadge(true);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
- NotificationRecord r = getNotificationRecord(channel);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
extractor.process(r);
@@ -140,13 +127,23 @@
BadgeExtractor extractor = new BadgeExtractor();
extractor.setConfig(mConfig);
+ when(mConfig.badgingEnabled(mUser)).thenReturn(true);
when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
- NotificationChannel channel =
- new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
- channel.setShowBadge(false);
- when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+ NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- NotificationRecord r = getNotificationRecord(channel);
+ extractor.process(r);
+
+ assertFalse(r.canShowBadge());
+ }
+
+ @Test
+ public void testAppYesChannelYesUserNo() throws Exception {
+ BadgeExtractor extractor = new BadgeExtractor();
+ extractor.setConfig(mConfig);
+
+ when(mConfig.badgingEnabled(mUser)).thenReturn(false);
+ when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+ NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
extractor.process(r);
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index d4904f5..39caa3c 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -34,7 +34,6 @@
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
-import android.content.Context;
import android.app.NotificationChannel;
import android.graphics.Color;
import android.media.AudioAttributes;
@@ -47,7 +46,6 @@
import android.os.VibrationEffect;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -69,7 +67,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BuzzBeepBlinkTest {
+public class BuzzBeepBlinkTest extends NotificationTestCase {
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
@@ -328,10 +326,6 @@
eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index 24cb72e..f92bd84 100644
--- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -25,7 +25,6 @@
import android.app.NotificationManager;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -38,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GlobalSortKeyComparatorTest {
+public class GlobalSortKeyComparatorTest extends NotificationTestCase {
private final String PKG = "PKG";
private final int UID = 1111111;
@@ -46,24 +45,23 @@
@Test
public void testComparator() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
left.setGlobalSortKey("first");
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
right.setGlobalSortKey("second");
- NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord last = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
@@ -86,16 +84,15 @@
@Test
public void testNoCrash_leftNull() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
@@ -117,17 +114,16 @@
@Test
public void testNoCrash_rightNull() throws Exception {
- Notification n = new Notification.Builder(
- InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.build();
- NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord left = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
"", 1499), getDefaultChannel());
left.setGlobalSortKey("not null");
- NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ NotificationRecord right = new NotificationRecord(getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
index 05c33a4..8dd1779 100644
--- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
@@ -34,10 +34,8 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -46,15 +44,11 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GroupHelperTest {
+public class GroupHelperTest extends NotificationTestCase {
private @Mock GroupHelper.Callback mCallback;
private GroupHelper mGroupHelper;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index 3dbd803..d325e10 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -24,10 +24,8 @@
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.NotificationChannel;
-import android.content.Context;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -43,7 +41,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ImportanceExtractorTest {
+public class ImportanceExtractorTest extends NotificationTestCase {
@Mock RankingConfig mConfig;
@@ -75,10 +73,6 @@
return r;
}
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
//
// Tests
//
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index dde08fc..1e5f96f 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -35,7 +35,6 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -51,7 +50,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationComparatorTest {
+public class NotificationComparatorTest extends NotificationTestCase {
@Mock Context mContext;
@Mock TelecomManager mTm;
@Mock RankingHandler handler;
@@ -83,10 +82,8 @@
MockitoAnnotations.initMocks(this);
int userId = UserHandle.myUserId();
- when(mContext.getResources()).thenReturn(
- InstrumentationRegistry.getTargetContext().getResources());
- when(mContext.getContentResolver()).thenReturn(
- InstrumentationRegistry.getTargetContext().getContentResolver());
+ when(mContext.getResources()).thenReturn(getContext().getResources());
+ when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index f0f4c4d..725e8f2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -38,7 +38,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationListenerServiceTest {
+public class NotificationListenerServiceTest extends NotificationTestCase {
private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 177c02d..9afb2d2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -52,9 +52,9 @@
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -75,7 +75,7 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class NotificationManagerServiceTest {
+public class NotificationManagerServiceTest extends NotificationTestCase {
private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int uid = Binder.getCallingUid();
@@ -86,7 +86,7 @@
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
- private Context mContext = InstrumentationRegistry.getTargetContext();
+ private Context mContext = getContext();
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@Mock
@@ -122,6 +122,12 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+
+ // most tests assume badging is enabled
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ UserHandle.getUserHandleForUid(uid).getIdentifier());
+
mNotificationManagerService = new TestableNotificationManagerService(mContext);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 1c8ca84..267d2a6 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -40,7 +40,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -56,7 +55,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationRecordTest {
+public class NotificationRecordTest extends NotificationTestCase {
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
@@ -96,8 +95,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mMockContext.getResources()).thenReturn(
- InstrumentationRegistry.getContext().getResources());
+ when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
new file mode 100644
index 0000000..cc30aab
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.notification;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.testing.TestableContext;
+
+import org.junit.Rule;
+
+
+public class NotificationTestCase {
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ protected Context getContext() {
+ return mContext;
+ }
+}
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 73372d5..0f8c815 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -49,12 +49,16 @@
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
+import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+import android.testing.TestableSettingsProvider;
import android.util.ArrayMap;
+import android.util.Slog;
import android.util.Xml;
import java.io.BufferedInputStream;
@@ -80,16 +84,19 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RankingHelperTest {
+public class RankingHelperTest extends NotificationTestCase {
private static final String PKG = "com.android.server.notification";
private static final int UID = 0;
+ private static final UserHandle USER = UserHandle.getUserHandleForUid(UID);
private static final String UPDATED_PKG = "updatedPkg";
private static final int UID2 = 1111111;
+ private static final UserHandle USER2 = UserHandle.getUserHandleForUid(UID2);
private static final String TEST_CHANNEL_ID = "test_channel_id";
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
+ @Mock Context mContext;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;
@@ -104,69 +111,11 @@
private RankingHelper mHelper;
private AudioAttributes mAudioAttributes;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
UserHandle user = UserHandle.ALL;
- mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
- new String[] {ImportanceExtractor.class.getName()});
-
- mNotiGroupGSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
- .setContentTitle("A")
- .setGroup("G")
- .setSortKey("A")
- .setWhen(1205)
- .build();
- mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
- "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel());
-
- mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
- .setContentTitle("B")
- .setGroup("G")
- .setSortKey("B")
- .setWhen(1200)
- .build();
- mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
- "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
- null, System.currentTimeMillis()), getDefaultChannel());
-
- mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
- .setContentTitle("C")
- .setWhen(1201)
- .build();
- mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
- "package", "package", 1, null, 0, 0, mNotiNoGroup, user,
- null, System.currentTimeMillis()), getDefaultChannel());
-
- mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
- .setContentTitle("D")
- .setWhen(1202)
- .build();
- mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
- "package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
- null, System.currentTimeMillis()), getDefaultChannel());
-
- mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
- .setContentTitle("E")
- .setWhen(1201)
- .setSortKey("A")
- .build();
- mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
- "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel());
-
- mAudioAttributes = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
- .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
- .build();
-
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
final ApplicationInfo upgrade = new ApplicationInfo();
@@ -174,6 +123,67 @@
when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
+ when(mContext.getResources()).thenReturn(
+ InstrumentationRegistry.getContext().getResources());
+ when(mContext.getPackageManager()).thenReturn(mPm);
+ when(mContext.getApplicationInfo()).thenReturn(legacy);
+ // most tests assume badging is enabled
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+
+ mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
+ new String[] {ImportanceExtractor.class.getName()});
+
+ mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("A")
+ .setGroup("G")
+ .setSortKey("A")
+ .setWhen(1205)
+ .build();
+ mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
+ null, System.currentTimeMillis()), getDefaultChannel());
+
+ mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("B")
+ .setGroup("G")
+ .setSortKey("B")
+ .setWhen(1200)
+ .build();
+ mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
+ null, System.currentTimeMillis()), getDefaultChannel());
+
+ mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("C")
+ .setWhen(1201)
+ .build();
+ mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
+ null, System.currentTimeMillis()), getDefaultChannel());
+
+ mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("D")
+ .setWhen(1202)
+ .build();
+ mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
+ null, System.currentTimeMillis()), getDefaultChannel());
+
+ mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("E")
+ .setWhen(1201)
+ .setSortKey("A")
+ .build();
+ mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
+ null, System.currentTimeMillis()), getDefaultChannel());
+
+ mAudioAttributes = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+ .build();
}
private NotificationChannel getDefaultChannel() {
@@ -229,6 +239,10 @@
assertEquals(expected.getName(), actual.getName());
}
+ private NotificationChannel getChannel() {
+ return new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ }
+
@Test
public void testFindAfterRankingWithASplitGroup() throws Exception {
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -644,14 +658,119 @@
}
@Test
+ public void testClearLockedFields() throws Exception {
+ final NotificationChannel channel = getChannel();
+ mHelper.clearLockedFields(channel);
+ assertEquals(0, channel.getUserLockedFields());
+
+ channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_IMPORTANCE);
+ mHelper.clearLockedFields(channel);
+ assertEquals(0, channel.getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_soundAndVibration() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setSound(new Uri.Builder().scheme("test").build(),
+ new AudioAttributes.Builder().build());
+ update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); // should be ignored
+ mHelper.updateNotificationChannel(PKG, UID, update1);
+ assertEquals(NotificationChannel.USER_LOCKED_SOUND,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ NotificationChannel update2 = getChannel();
+ update2.enableVibration(true);
+ mHelper.updateNotificationChannel(PKG, UID, update2);
+ assertEquals(NotificationChannel.USER_LOCKED_SOUND
+ | NotificationChannel.USER_LOCKED_VIBRATION,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_vibrationAndLights() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setVibrationPattern(new long[]{7945, 46 ,246});
+ mHelper.updateNotificationChannel(PKG, UID, update1);
+ assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.enableLights(true);
+ mHelper.updateNotificationChannel(PKG, UID, update2);
+ assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
+ | NotificationChannel.USER_LOCKED_LIGHTS,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_lightsAndImportance() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setLightColor(Color.GREEN);
+ mHelper.updateNotificationChannel(PKG, UID, update1);
+ assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.setImportance(IMPORTANCE_DEFAULT);
+ mHelper.updateNotificationChannel(PKG, UID, update2);
+ assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
+ | NotificationChannel.USER_LOCKED_IMPORTANCE,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+ assertEquals(0,
+ mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update1 = getChannel();
+ update1.setBypassDnd(true);
+ mHelper.updateNotificationChannel(PKG, UID, update1);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ mHelper.updateNotificationChannel(PKG, UID, update2);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update3 = getChannel();
+ update3.setShowBadge(false);
+ mHelper.updateNotificationChannel(PKG, UID, update3);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY
+ | NotificationChannel.USER_LOCKED_SHOW_BADGE,
+ mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
public void testDeleteNonExistentChannel() throws Exception {
mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
}
@Test
public void testGetDeletedChannel() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ NotificationChannel channel = getChannel();
channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
channel.enableLights(true);
channel.setBypassDnd(true);
@@ -1093,4 +1212,36 @@
object.getInt("channelCount"));
}
}
+
+ @Test
+ public void testBadgingOverrideTrue() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertTrue(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingOverrideFalse() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingOverrideUserIsolation() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER2.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ assertTrue(mHelper.badgingEnabled(USER2));
+ }
+
}
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
index 07f3162..e354267 100644
--- a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
@@ -26,7 +26,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RateEstimatorTest {
+public class RateEstimatorTest extends NotificationTestCase {
private long mTestStartTime;
private RateEstimator mEstimator;
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index bc25860..07b21fb 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -27,11 +27,9 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Slog;
@@ -51,7 +49,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SnoozeHelperTest {
+public class SnoozeHelperTest extends NotificationTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
@Mock SnoozeHelper.Callback mCallback;
@@ -60,10 +58,6 @@
private SnoozeHelper mSnoozeHelper;
- private Context getContext() {
- return InstrumentationRegistry.getTargetContext();
- }
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index d09b858..4ac0c65 100644
--- a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -32,7 +32,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ValidateNotificationPeopleTest {
+public class ValidateNotificationPeopleTest extends NotificationTestCase {
@Test
public void testNoExtra() throws Exception {
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index 13056cf..fe97bca1 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentProvider;
import android.util.Log;
@@ -48,9 +49,10 @@
}
void clearValuesAndCheck(Context context) {
- mValues.put(key("global", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
- mValues.put(key("secure", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
- mValues.put(key("system", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
+ int userId = UserHandle.myUserId();
+ mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+ mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+ mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
// Verify that if any test is using TestableContext, they all have the correct settings
// provider.
@@ -66,11 +68,12 @@
public Bundle call(String method, String arg, Bundle extras) {
// Methods are "GET_system", "GET_global", "PUT_secure", etc.
+ final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
final String[] commands = method.split("_", 2);
final String op = commands[0];
final String table = commands[1];
- String k = key(table, arg);
+ String k = key(table, arg, userId);
String value;
Bundle out = new Bundle();
switch (op) {
@@ -103,8 +106,13 @@
return out;
}
- private static String key(String table, String key) {
- return table + "_" + key;
+ private static String key(String table, String key, int userId) {
+ if ("global".equals(table)) {
+ return table + "_" + key;
+ } else {
+ return table + "_" + userId + "_" + key;
+ }
+
}
/**
diff --git a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
index 1f71867..0e2cc57 100644
--- a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
@@ -66,6 +66,16 @@
}
@Test
+ public void testSeparateUsers() {
+ Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "something", 0);
+ Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "else", 1);
+ assertEquals("something",
+ Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 0));
+ assertEquals("else",
+ Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 1));
+ }
+
+ @Test
public void testPassThrough() {
// Grab the value of a setting that is not overridden.
assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 15648bd..ba73180 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -762,6 +762,15 @@
return 1;
}
+ // Now add any dependencies passed in.
+ for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
+ const String8& assetPath = bundle->getPackageIncludes()[i];
+ if (!assets.addAssetPath(assetPath, NULL)) {
+ fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
+ return 1;
+ }
+ }
+
// Make a dummy config for retrieving resources... we need to supply
// non-default values for some configs so that we can retrieve resources
// in the app that don't have a default. The most important of these is
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 01930d0..e45d142 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -27,7 +27,7 @@
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "14";
+static const char* sMinorVersion = "15";
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 5413b33..c192d69 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -114,6 +114,7 @@
std::string output_path;
Maybe<std::string> res_dir;
bool pseudolocalize = false;
+ bool no_png_crunch = false;
bool legacy_mode = false;
bool verbose = false;
};
@@ -663,6 +664,7 @@
"Generate resources for pseudo-locales "
"(en-XA and ar-XB)",
&options.pseudolocalize)
+ .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
.OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
&options.legacy_mode)
.OptionalSwitch("-v", "Enables verbose logging", &verbose);
@@ -738,7 +740,8 @@
if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
error = true;
}
- } else if (path_data.extension == "png" || path_data.extension == "9.png") {
+ } else if (!options.no_png_crunch &&
+ (path_data.extension == "png" || path_data.extension == "9.png")) {
if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
error = true;
}
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 0291720..e92565f 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,13 @@
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
+## Version 2.15
+### `aapt2 compile ...`
+- Add `--no-crunch` option to avoid processing PNGs during the compile phase. Note that this
+ shouldn't be used as a performance optimization, as once the PNG is processed, its result is
+ cached for incremental linking. This should only be used if the developer has specially
+ pre-processed the PNG and wants it byte-for-byte identical to the input.
+ NOTE: 9-patches will not be processed correctly with this flag set.
+
## Version 2.14
### `aapt2 link ...`
- If an app is building with a minSdkVersion < 26 and a --package-id XX where XX > 7F, aapt2
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
deleted file mode 100644
index 2852302..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
deleted file mode 100644
index b7bf5b4..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
+++ /dev/null
Binary files differ
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 2b5e0f9..7b565b1 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
@@ -389,7 +389,8 @@
assertTrue(sRenderMessages.isEmpty());
}
- @Test
+ //@Test
+ // Temporarily disabled (b/37725933)
public void testFonts() throws ClassNotFoundException {
// TODO: styles seem to be broken in TextView
renderAndVerify("fonts_test.xml", "font_test.png");
diff --git a/vr/Android.mk b/vr/Android.mk
new file mode 100644
index 0000000..5b65d3f
--- /dev/null
+++ b/vr/Android.mk
@@ -0,0 +1,38 @@
+# 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.
+LOCAL_PATH := $(call my-dir)
+
+# Library to perform dlopen on the actual shared library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvr_loader
+LOCAL_MODULE_OWNER := google
+LOCAL_SRC_FILES := dvr_library_loader.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+# Java platform library for vr stuff.
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.google.vr.platform
+LOCAL_MODULE_OWNER := google
+LOCAL_REQUIRED_MODULES := libdvr_loader libdvr
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.google.vr.platform.xml
+LOCAL_SRC_FILES := com.google.vr.platform.xml
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_OWNER := google
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+include $(BUILD_PREBUILT)
diff --git a/vr/com.google.vr.platform.xml b/vr/com.google.vr.platform.xml
new file mode 100644
index 0000000..952b476
--- /dev/null
+++ b/vr/com.google.vr.platform.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<permissions>
+ <library name="com.google.vr.platform"
+ file="/system/framework/com.google.vr.platform.jar" />
+</permissions>
diff --git a/vr/dvr_library_loader.cpp b/vr/dvr_library_loader.cpp
new file mode 100644
index 0000000..0b4298a
--- /dev/null
+++ b/vr/dvr_library_loader.cpp
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <string>
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL
+Java_com_google_vr_platform_Dvr_nativeLoadLibrary(
+ JNIEnv* env, jclass, jstring java_library) {
+ if (!java_library)
+ return 0;
+
+ // Convert the Java String object to a C++ null-terminated string.
+ const char* data = env->GetStringUTFChars(java_library, NULL);
+ size_t size = env->GetStringUTFLength(java_library);
+ std::string library(data, size);
+ env->ReleaseStringUTFChars(java_library, data);
+
+ // Return the handle to the requested library.
+ return reinterpret_cast<jlong>(
+ dlopen(library.c_str(), RTLD_NOW | RTLD_LOCAL));
+}
+
+} // extern "C"
diff --git a/vr/java/com/google/vr/platform/DeviceInfo.java b/vr/java/com/google/vr/platform/DeviceInfo.java
new file mode 100644
index 0000000..f6da66b
--- /dev/null
+++ b/vr/java/com/google/vr/platform/DeviceInfo.java
@@ -0,0 +1,19 @@
+package com.google.vr.platform;
+
+import android.os.SystemProperties;
+
+/**
+ * Class to get information about the vr device.
+ * @hide
+ */
+public class DeviceInfo {
+
+ private static final String VR_MODE_BOOT = "ro.boot.vr";
+
+ /**
+ * Returns true if this device boots directly in VR mode.
+ */
+ public static boolean getVrBoot() {
+ return SystemProperties.getBoolean(VR_MODE_BOOT, false);
+ }
+}
diff --git a/vr/java/com/google/vr/platform/Dvr.java b/vr/java/com/google/vr/platform/Dvr.java
new file mode 100644
index 0000000..b07d634
--- /dev/null
+++ b/vr/java/com/google/vr/platform/Dvr.java
@@ -0,0 +1,22 @@
+package com.google.vr.platform;
+
+/**
+ * Class to load the dvr api.
+ * @hide
+ */
+public class Dvr {
+ /**
+ * Opens a shared library containing the dvr api and returns the handle to it.
+ *
+ * @return A Long object describing the handle returned by dlopen.
+ */
+ public static Long loadLibrary() {
+ // Load a thin JNI library that runs dlopen on request.
+ System.loadLibrary("dvr_loader");
+
+ // Performs dlopen on the library and returns the handle.
+ return nativeLoadLibrary("libdvr.so");
+ }
+
+ private static native long nativeLoadLibrary(String library);
+}