diff options
104 files changed, 2219 insertions, 717 deletions
diff --git a/api/current.txt b/api/current.txt index 5a5f9bc96b3f..9df1764380f4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9925,6 +9925,15 @@ package android.content.pm { method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo); } + public final class ChangedPackages implements android.os.Parcelable { + ctor public ChangedPackages(int, java.util.List<java.lang.String>); + method public int describeContents(); + method public java.util.List<java.lang.String> getPackageNames(); + method public int getSequenceNumber(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { ctor public ComponentInfo(); ctor public ComponentInfo(android.content.pm.ComponentInfo); @@ -10271,6 +10280,7 @@ package android.content.pm { method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.ChangedPackages getChangedPackages(int); method public abstract int getComponentEnabledSetting(android.content.ComponentName); method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo); @@ -14432,6 +14442,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -14460,6 +14471,7 @@ package android.hardware { field public static final int TYPE_HEART_RATE = 21; // 0x15 field public static final int TYPE_LIGHT = 5; // 0x5 field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa + field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22 field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2 field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe field public static final int TYPE_MOTION_DETECT = 30; // 0x1e @@ -20713,7 +20725,7 @@ package android.media { method public int getStreamMaxVolume(int); method public int getStreamVolume(int); method public deprecated int getVibrateSetting(int); - method public boolean isBluetoothA2dpOn(); + method public deprecated boolean isBluetoothA2dpOn(); method public boolean isBluetoothScoAvailableOffCall(); method public boolean isBluetoothScoOn(); method public boolean isMicrophoneMute(); @@ -40063,6 +40075,7 @@ package android.test.mock { method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public android.content.pm.ChangedPackages getChangedPackages(int); method public int getComponentEnabledSetting(android.content.ComponentName); method public android.graphics.drawable.Drawable getDefaultActivityIcon(); method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo); @@ -47316,7 +47329,7 @@ package android.view.textclassifier { public final class TextClassificationManager { method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier(); } public final class TextClassificationResult { @@ -52450,6 +52463,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -54336,6 +54356,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -54464,6 +54499,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index acbd8b9af7e2..eed1718f299d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10373,6 +10373,15 @@ package android.content.pm { method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo); } + public final class ChangedPackages implements android.os.Parcelable { + ctor public ChangedPackages(int, java.util.List<java.lang.String>); + method public int describeContents(); + method public java.util.List<java.lang.String> getPackageNames(); + method public int getSequenceNumber(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { ctor public ComponentInfo(); ctor public ComponentInfo(android.content.pm.ComponentInfo); @@ -10770,6 +10779,7 @@ package android.content.pm { method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.ChangedPackages getChangedPackages(int); method public abstract int getComponentEnabledSetting(android.content.ComponentName); method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int); @@ -15026,6 +15036,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -15056,6 +15067,7 @@ package android.hardware { field public static final int TYPE_HEART_RATE = 21; // 0x15 field public static final int TYPE_LIGHT = 5; // 0x5 field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa + field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22 field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2 field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe field public static final int TYPE_MOTION_DETECT = 30; // 0x1e @@ -22327,7 +22339,7 @@ package android.media { method public int getStreamMaxVolume(int); method public int getStreamVolume(int); method public deprecated int getVibrateSetting(int); - method public boolean isBluetoothA2dpOn(); + method public deprecated boolean isBluetoothA2dpOn(); method public boolean isBluetoothScoAvailableOffCall(); method public boolean isBluetoothScoOn(); method public boolean isHdmiSystemAudioSupported(); @@ -43488,6 +43500,7 @@ package android.test.mock { method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public android.content.pm.ChangedPackages getChangedPackages(int); method public int getComponentEnabledSetting(android.content.ComponentName); method public android.graphics.drawable.Drawable getDefaultActivityIcon(); method public java.lang.String getDefaultBrowserPackageNameAsUser(int); @@ -50757,7 +50770,7 @@ package android.view.textclassifier { public final class TextClassificationManager { method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier(); } public final class TextClassificationResult { @@ -56252,6 +56265,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -58138,6 +58158,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -58266,6 +58301,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 4de1b1d93d14..114060d6a116 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -9953,6 +9953,15 @@ package android.content.pm { method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo); } + public final class ChangedPackages implements android.os.Parcelable { + ctor public ChangedPackages(int, java.util.List<java.lang.String>); + method public int describeContents(); + method public java.util.List<java.lang.String> getPackageNames(); + method public int getSequenceNumber(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.pm.ChangedPackages> CREATOR; + } + public class ComponentInfo extends android.content.pm.PackageItemInfo { ctor public ComponentInfo(); ctor public ComponentInfo(android.content.pm.ComponentInfo); @@ -10300,6 +10309,7 @@ package android.content.pm { method public abstract java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.ChangedPackages getChangedPackages(int); method public abstract int getComponentEnabledSetting(android.content.ComponentName); method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int); @@ -14467,6 +14477,7 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; field public static final java.lang.String STRING_TYPE_LIGHT = "android.sensor.light"; field public static final java.lang.String STRING_TYPE_LINEAR_ACCELERATION = "android.sensor.linear_acceleration"; + field public static final java.lang.String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = "android.sensor.low_latency_offbody"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; field public static final java.lang.String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.magnetic_field_uncalibrated"; field public static final java.lang.String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; @@ -14495,6 +14506,7 @@ package android.hardware { field public static final int TYPE_HEART_RATE = 21; // 0x15 field public static final int TYPE_LIGHT = 5; // 0x5 field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa + field public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; // 0x22 field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2 field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe field public static final int TYPE_MOTION_DETECT = 30; // 0x1e @@ -20806,7 +20818,7 @@ package android.media { method public int getStreamMaxVolume(int); method public int getStreamVolume(int); method public deprecated int getVibrateSetting(int); - method public boolean isBluetoothA2dpOn(); + method public deprecated boolean isBluetoothA2dpOn(); method public boolean isBluetoothScoAvailableOffCall(); method public boolean isBluetoothScoOn(); method public boolean isMicrophoneMute(); @@ -40200,6 +40212,7 @@ package android.test.mock { method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public android.content.pm.ChangedPackages getChangedPackages(int); method public int getComponentEnabledSetting(android.content.ComponentName); method public android.graphics.drawable.Drawable getDefaultActivityIcon(); method public java.lang.String getDefaultBrowserPackageNameAsUser(int); @@ -47630,7 +47643,7 @@ package android.view.textclassifier { public final class TextClassificationManager { method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence); - method public android.view.textclassifier.TextClassifier getDefaultTextClassifier(); + method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier(); } public final class TextClassificationResult { @@ -52779,6 +52792,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -54665,6 +54685,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -54793,6 +54828,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 78cd89bb14ca..fca26f87ce4f 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1865,7 +1865,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim if (mEvent == ANIMATION_START) { return mNode.mStartTime; } else if (mEvent == ANIMATION_DELAY_ENDED) { - return mNode.mStartTime = mNode.mStartTime == DURATION_INFINITE + return mNode.mStartTime == DURATION_INFINITE ? DURATION_INFINITE : mNode.mStartTime + mNode.mAnimation.getStartDelay(); } else { return mNode.mEndTime; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index efe72c306883..fda99660ff74 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -133,7 +133,7 @@ public class ActivityManager { public final static boolean ENABLE_TASK_SNAPSHOTS; static { - ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", true); + ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", false); } static final class UidObserver extends IUidObserver.Stub { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 376823ed09da..1f8e6db236ae 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5726,7 +5726,7 @@ public final class ActivityThread { // Preload fonts resources try { final ApplicationInfo info = - sPackageManager.getApplicationInfo( + getPackageManager().getApplicationInfo( data.appInfo.packageName, PackageManager.GET_META_DATA /*flags*/, UserHandle.myUserId()); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0c6c4ba33ee3..333e412e1c33 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -29,6 +29,7 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; import android.content.pm.ComponentInfo; import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; @@ -506,6 +507,15 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public ChangedPackages getChangedPackages(int sequenceNumber) { + try { + return mPM.getChangedPackages(sequenceNumber, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override @SuppressWarnings("unchecked") public FeatureInfo[] getSystemAvailableFeatures() { try { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 6719be4059c5..6717491e55f4 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -210,6 +210,7 @@ interface IActivityManager { boolean killPids(in int[] pids, in String reason, boolean secure); List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags); ActivityManager.TaskThumbnail getTaskThumbnail(int taskId); + ActivityManager.TaskDescription getTaskDescription(int taskId); // Retrieve running application processes in the system List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses(); // Get device configuration diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 7900fc5a69cb..45a46b302178 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4082,16 +4082,23 @@ public class Notification implements Parcelable /** * Construct a RemoteViews for the final heads-up notification layout. + * + * @param increasedHeight true if this layout be created with an increased height. Some + * styles may support showing more then just that basic 1U size + * and the system may decide to render important notifications + * slightly bigger even when collapsed. + * + * @hide */ - public RemoteViews createHeadsUpContentView() { + public RemoteViews createHeadsUpContentView(boolean increasedHeight) { if (mN.headsUpContentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) { return mN.headsUpContentView; } else if (mStyle != null) { - final RemoteViews styleView = mStyle.makeHeadsUpContentView(); - if (styleView != null) { - return styleView; - } + final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight); + if (styleView != null) { + return styleView; + } } else if (mActions.size() == 0) { return null; } @@ -4100,6 +4107,13 @@ public class Notification implements Parcelable } /** + * Construct a RemoteViews for the final heads-up notification layout. + */ + public RemoteViews createHeadsUpContentView() { + return createHeadsUpContentView(false /* useIncreasedHeight */); + } + + /** * Construct a RemoteViews for the display in public contexts like on the lockscreen. * * @hide @@ -4823,9 +4837,11 @@ public class Notification implements Parcelable /** * Construct a Style-specific RemoteViews for the final HUN layout. + * + * @param increasedHeight true if this layout be created with an increased height. * @hide */ - public RemoteViews makeHeadsUpContentView() { + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { return null; } @@ -5171,6 +5187,17 @@ public class Notification implements Parcelable /** * @hide */ + @Override + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { + if (increasedHeight && mBuilder.mActions.size() > 0) { + return makeBigContentView(); + } + return super.makeHeadsUpContentView(increasedHeight); + } + + /** + * @hide + */ public RemoteViews makeBigContentView() { // Nasty @@ -5578,7 +5605,10 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeHeadsUpContentView() { + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { + if (increasedHeight) { + return makeBigContentView(); + } Message m = findLatestIncomingMessage(); CharSequence title = mConversationTitle != null ? mConversationTitle @@ -6028,7 +6058,7 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeHeadsUpContentView() { + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { RemoteViews expanded = makeMediaBigContentView(); return expanded != null ? expanded : makeMediaContentView(); } @@ -6208,7 +6238,7 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeHeadsUpContentView() { + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { return makeDecoratedHeadsUpContentView(); } @@ -6344,7 +6374,7 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeHeadsUpContentView() { + public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null ? mBuilder.mN.headsUpContentView : mBuilder.mN.contentView; diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java index 0ae85056da56..6ee478059171 100644 --- a/core/java/android/app/QueuedWork.java +++ b/core/java/android/app/QueuedWork.java @@ -16,197 +16,86 @@ package android.app; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.util.LinkedList; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** - * Internal utility class to keep track of process-global work that's outstanding and hasn't been - * finished yet. - * - * New work will be {@link #queue queued}. + * Internal utility class to keep track of process-global work that's + * outstanding and hasn't been finished yet. * - * It is possible to add 'finisher'-runnables that are {@link #waitToFinish guaranteed to be run}. - * This is used to make sure the work has been finished. - * - * This was created for writing SharedPreference edits out asynchronously so we'd have a mechanism - * to wait for the writes in Activity.onPause and similar places, but we may use this mechanism for - * other things in the future. - * - * The queued asynchronous work is performed on a separate, dedicated thread. + * This was created for writing SharedPreference edits out + * asynchronously so we'd have a mechanism to wait for the writes in + * Activity.onPause and similar places, but we may use this mechanism + * for other things in the future. * * @hide */ public class QueuedWork { - private static final String LOG_TAG = QueuedWork.class.getSimpleName(); - - /** Delay for delayed runnables */ - private static final long DELAY = 50; - /** Lock for this class */ - private static final Object sLock = new Object(); + // The set of Runnables that will finish or wait on any async + // activities started by the application. + private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers = + new ConcurrentLinkedQueue<Runnable>(); - /** Finishers {@link #addFinisher added} and not yet {@link #removeFinisher removed} */ - @GuardedBy("sLock") - private static final LinkedList<Runnable> sFinishers = new LinkedList<>(); - - /** {@link #getHandler() Lazily} created handler */ - @GuardedBy("sLock") - private static Handler sHandler = null; - - /** Work queued via {@link #queue} */ - @GuardedBy("sLock") - private static final LinkedList<Runnable> sWork = new LinkedList<>(); - - /** If new work can be delayed or not */ - @GuardedBy("sLock") - private static boolean sCanDelay = true; + private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class /** - * Lazily create a handler on a separate thread. - * - * @return the handler + * Returns a single-thread Executor shared by the entire process, + * creating it if necessary. */ - private static Handler getHandler() { - synchronized (sLock) { - if (sHandler == null) { - HandlerThread handlerThread = new HandlerThread("queued-work-looper", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - - sHandler = new QueuedWorkHandler(handlerThread.getLooper()); + public static ExecutorService singleThreadExecutor() { + synchronized (QueuedWork.class) { + if (sSingleThreadExecutor == null) { + // TODO: can we give this single thread a thread name? + sSingleThreadExecutor = Executors.newSingleThreadExecutor(); } - return sHandler; + return sSingleThreadExecutor; } } /** - * Add a finisher-runnable to wait for {@link #queue asynchronously processed work}. - * - * Used by SharedPreferences$Editor#startCommit(). + * Add a runnable to finish (or wait for) a deferred operation + * started in this context earlier. Typically finished by e.g. + * an Activity#onPause. Used by SharedPreferences$Editor#startCommit(). * - * Note that this doesn't actually start it running. This is just a scratch set for callers - * doing async work to keep updated with what's in-flight. In the common case, caller code - * (e.g. SharedPreferences) will pretty quickly call remove() after an add(). The only time - * these Runnables are run is from {@link #waitToFinish}. - * - * @param finisher The runnable to add as finisher + * Note that this doesn't actually start it running. This is just + * a scratch set for callers doing async work to keep updated with + * what's in-flight. In the common case, caller code + * (e.g. SharedPreferences) will pretty quickly call remove() + * after an add(). The only time these Runnables are run is from + * waitToFinish(), below. */ - public static void addFinisher(Runnable finisher) { - synchronized (sLock) { - sFinishers.add(finisher); - } + public static void add(Runnable finisher) { + sPendingWorkFinishers.add(finisher); } - /** - * Remove a previously {@link #addFinisher added} finisher-runnable. - * - * @param finisher The runnable to remove. - */ - public static void removeFinisher(Runnable finisher) { - synchronized (sLock) { - sFinishers.remove(finisher); - } + public static void remove(Runnable finisher) { + sPendingWorkFinishers.remove(finisher); } /** - * Trigger queued work to be processed immediately. The queued work is processed on a separate - * thread asynchronous. While doing that run and process all finishers on this thread. The - * finishers can be implemented in a way to check weather the queued work is finished. + * Finishes or waits for async operations to complete. + * (e.g. SharedPreferences$Editor#startCommit writes) * - * Is called from the Activity base class's onPause(), after BroadcastReceiver's onReceive, - * after Service command handling, etc. (so async work is never lost) + * Is called from the Activity base class's onPause(), after + * BroadcastReceiver's onReceive, after Service command handling, + * etc. (so async work is never lost) */ public static void waitToFinish() { - Handler handler = getHandler(); - - synchronized (sLock) { - if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) { - // Force the delayed work to be processed now - handler.removeMessages(QueuedWorkHandler.MSG_RUN); - handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN); - } - - // We should not delay any work as this might delay the finishers - sCanDelay = false; - } - - try { - while (true) { - Runnable finisher; - - synchronized (sLock) { - finisher = sFinishers.poll(); - } - - if (finisher == null) { - break; - } - - finisher.run(); - } - } finally { - sCanDelay = true; - } - } - - /** - * Queue a work-runnable for processing asynchronously. - * - * @param work The new runnable to process - * @param shouldDelay If the message should be delayed - */ - public static void queue(Runnable work, boolean shouldDelay) { - Handler handler = getHandler(); - - synchronized (sLock) { - sWork.add(work); - - if (shouldDelay && sCanDelay) { - handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY); - } else { - handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN); - } + Runnable toFinish; + while ((toFinish = sPendingWorkFinishers.poll()) != null) { + toFinish.run(); } } - + /** - * @return True iff there is any {@link #queue async work queued}. + * Returns true if there is pending work to be done. Note that the + * result is out of data as soon as you receive it, so be careful how you + * use it. */ public static boolean hasPendingWork() { - synchronized (sLock) { - return !sWork.isEmpty(); - } - } - - private static class QueuedWorkHandler extends Handler { - static final int MSG_RUN = 1; - - QueuedWorkHandler(Looper looper) { - super(looper); - } - - public void handleMessage(Message msg) { - if (msg.what == MSG_RUN) { - LinkedList<Runnable> work; - - synchronized (sWork) { - work = (LinkedList<Runnable>) sWork.clone(); - sWork.clear(); - - // Remove all msg-s as all work will be processed now - removeMessages(MSG_RUN); - } - - work.forEach(Runnable::run); - } - } + return !sPendingWorkFinishers.isEmpty(); } + } diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java index 023b4f38ff1c..3bb7019c212d 100644 --- a/core/java/android/app/SharedPreferencesImpl.java +++ b/core/java/android/app/SharedPreferencesImpl.java @@ -406,12 +406,12 @@ final class SharedPreferencesImpl implements SharedPreferences { } }; - QueuedWork.addFinisher(awaitCommit); + QueuedWork.add(awaitCommit); Runnable postWriteRunnable = new Runnable() { public void run() { awaitCommit.run(); - QueuedWork.removeFinisher(awaitCommit); + QueuedWork.remove(awaitCommit); } }; @@ -588,10 +588,10 @@ final class SharedPreferencesImpl implements SharedPreferences { } if (DEBUG) { - Log.d(TAG, "queued " + mcr.memoryStateGeneration + " -> " + mFile.getName()); + Log.d(TAG, "added " + mcr.memoryStateGeneration + " -> " + mFile.getName()); } - QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit); + QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable); } private static FileOutputStream createFileOutputStream(File file) { diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 08aa5f2589bf..6591fc9671f5 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -420,18 +420,17 @@ public class AssistStructure implements Parcelable { mRoot = new ViewNode(); ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false, 0); - if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) { - // This is a secure window, so it doesn't want a screenshot, and that - // means we should also not copy out its view hierarchy. - + if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) { if (forAutoFill) { // NOTE: flags are currently not supported, hence 0 view.onProvideAutoFillStructure(builder, 0); } else { + // This is a secure window, so it doesn't want a screenshot, and that + // means we should also not copy out its view hierarchy for Assist view.onProvideStructure(builder); + builder.setAssistBlocked(true); + return; } - builder.setAssistBlocked(true); - return; } if (forAutoFill) { // NOTE: flags are currently not supported, hence 0 diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index c3d6606089cb..485d0784b766 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -211,16 +211,16 @@ public abstract class BroadcastReceiver { // of the list to finish the broadcast, so we don't block this // thread (which may be the main thread) to have it finished. // - // Note that we don't need to use QueuedWork.addFinisher() with the + // Note that we don't need to use QueuedWork.add() with the // runnable, since we know the AM is waiting for us until the // executor gets to it. - QueuedWork.queue(new Runnable() { + QueuedWork.singleThreadExecutor().execute( new Runnable() { @Override public void run() { if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, "Finishing broadcast after work to component " + mToken); sendFinished(mgr); } - }, false); + }); } else { if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, "Finishing broadcast to component " + mToken); diff --git a/core/java/android/content/pm/ChangedPackages.aidl b/core/java/android/content/pm/ChangedPackages.aidl new file mode 100644 index 000000000000..1a9f5a1505eb --- /dev/null +++ b/core/java/android/content/pm/ChangedPackages.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.content.pm; + +parcelable ChangedPackages;
\ No newline at end of file diff --git a/core/java/android/content/pm/ChangedPackages.java b/core/java/android/content/pm/ChangedPackages.java new file mode 100644 index 000000000000..94b8a5dfd5e0 --- /dev/null +++ b/core/java/android/content/pm/ChangedPackages.java @@ -0,0 +1,82 @@ +/** + * 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Packages that have been changed since the last time they + * were requested. + */ +public final class ChangedPackages implements Parcelable { + /** The last known sequence number for these changes */ + private final int mSequenceNumber; + /** The names of the packages that have changed */ + private final List<String> mPackageNames; + + public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) { + this.mSequenceNumber = sequenceNumber; + this.mPackageNames = packageNames; + } + + /** @hide */ + protected ChangedPackages(Parcel in) { + mSequenceNumber = in.readInt(); + mPackageNames = in.createStringArrayList(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSequenceNumber); + dest.writeStringList(mPackageNames); + } + + /** + * Returns the last known sequence number for these changes. + */ + public int getSequenceNumber() { + return mSequenceNumber; + } + + /** + * Returns the names of the packages that have changed. + */ + public @NonNull List<String> getPackageNames() { + return mPackageNames; + } + + public static final Parcelable.Creator<ChangedPackages> CREATOR = + new Parcelable.Creator<ChangedPackages>() { + public ChangedPackages createFromParcel(Parcel in) { + return new ChangedPackages(in); + } + + public ChangedPackages[] newArray(int size) { + return new ChangedPackages[size]; + } + }; +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 3fb46cfab791..9d36a730ac09 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -23,6 +23,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ContainerEncryptionParams; +import android.content.pm.ChangedPackages; import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver2; @@ -611,6 +612,8 @@ interface IPackageManager { String getServicesSystemSharedLibraryPackageName(); String getSharedSystemSharedLibraryPackageName(); + ChangedPackages getChangedPackages(int sequenceNumber, int userId); + boolean isPackageDeviceAdminOnAnyUser(String packageName); List<String> getPreviousCodePaths(in String packageName); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b20b5e2e9d96..308153dd3538 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.CheckResult; import android.annotation.DrawableRes; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -3853,6 +3854,17 @@ public abstract class PackageManager { public abstract @NonNull String getSharedSystemSharedLibraryPackageName(); /** + * Returns the names of the packages that have been changed + * [eg. added, removed or updated] since the given sequence + * number. + * <p>If no packages have been changed, returns <code>null</code>. + * <p>The sequence number starts at <code>0</code> and is + * reset every boot. + */ + public abstract @Nullable ChangedPackages getChangedPackages( + @IntRange(from=0) int sequenceNumber); + + /** * Get a list of features that are available on the * system. * diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index d87c55e1c7c7..8c13cc893cc7 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -649,9 +649,22 @@ public final class Sensor { * to be in the same order as the HAL. Skipping this sensor */ - /* TYPE_LOW_LATENCY_OFF_BODY_SENSOR - defined as type 34 in the HAL needs to - * be defined in this space. + /** + * A constant describing a low latency off-body detect sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + * */ + public static final int TYPE_LOW_LATENCY_OFFBODY_DETECT = 34; + + + /** + * A constant string describing a low-latency offbody detector sensor. + * + * @see #TYPE_LOW_LATENCY_OFFBODY_DETECT + */ + public static final String STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT = + "android.sensor.low_latency_offbody"; /** * A constant describing an uncalibrated accelerometer sensor. @@ -669,6 +682,7 @@ public final class Sensor { */ public static final String STRING_TYPE_ACCELEROMETER_UNCALIBRATED = "android.sensor.accelerometer_uncalibrated"; + /** * A constant describing all sensor types. */ @@ -786,7 +800,7 @@ public final class Sensor { 1, // SENSOR_TYPE_HEART_BEAT 2, // SENSOR_TYPE_DYNAMIC_SENSOR_META 16,// skip over additional sensor info type - 1, // reserving for LLOB sensor type + 1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT 6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED }; @@ -1195,6 +1209,9 @@ public final class Sensor { case TYPE_DYNAMIC_SENSOR_META: mStringType = STRING_TYPE_DYNAMIC_SENSOR_META; return true; + case TYPE_LOW_LATENCY_OFFBODY_DETECT: + mStringType = STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT; + return true; case TYPE_ACCELEROMETER_UNCALIBRATED: mStringType = STRING_TYPE_ACCELEROMETER_UNCALIBRATED; return true; diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 9b72757a8737..c0bca97e12b7 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -564,6 +564,42 @@ public class SensorEvent { * completely unlikely to be anywhere else on the QRS complex. * </p> * + * <h4>{@link android.hardware.Sensor#TYPE_LOW_LATENCY_OFFBODY_DETECT + * Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT}:</h4> + * + * <p> + * A sensor of this type returns an event every time the device transitions + * from off-body to on-body and from on-body to off-body (e.g. a wearable + * device being removed from the wrist would trigger an event indicating an + * off-body transition). The event returned will contain a single value to + * indicate off-body state: + * </p> + * + * <ul> + * <li> values[0]: off-body state</li> + * </ul> + * + * <p> + * Valid values for off-body state: + * <ul> + * <li> 1.0 (device is on-body)</li> + * <li> 0.0 (device is off-body)</li> + * </ul> + * </p> + * + * <p> + * When a sensor of this type is activated, it must deliver the initial + * on-body or off-body event representing the current device state within + * 5 seconds of activating the sensor. + * </p> + * + * <p> + * This sensor must be able to detect and report an on-body to off-body + * transition within 1 second of the device being removed from the body, + * and must be able to detect and report an off-body to on-body transition + * within 5 seconds of the device being put back onto the body. + * </p> + * * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_UNCALIBRATED * Sensor.TYPE_ACCELEROMETER_UNCALIBRATED}:</h4> All values are in SI * units (m/s^2) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index dc5750d55066..acb1d07c36a4 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1453,8 +1453,9 @@ public class ConnectivityManager { private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) { if (delay >= 0) { Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay); - Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); - sCallbackHandler.sendMessageDelayed(msg, delay); + CallbackHandler handler = getHandler(); + Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); + handler.sendMessageDelayed(msg, delay); } } @@ -2897,19 +2898,19 @@ public class ConnectivityManager { } } - static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); - static CallbackHandler sCallbackHandler; + private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); + private static CallbackHandler sCallbackHandler; - private final static int LISTEN = 1; - private final static int REQUEST = 2; + private static final int LISTEN = 1; + private static final int REQUEST = 2; private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, int timeoutMs, int action, int legacyType) { - return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType); + return sendRequestForNetwork(need, callback, timeoutMs, action, legacyType, getHandler()); } - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) { + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, + int timeoutMs, int action, int legacyType, CallbackHandler handler) { if (callback == null) { throw new IllegalArgumentException("null NetworkCallback"); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index fa9f394add60..b3366d883fc6 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -506,4 +506,24 @@ public class ZygoteProcess { state.writer.flush(); } } + + /** + * Instructs the zygote to preload the default set of classes and resources. Returns + * {@code true} if a preload was performed as a result of this call, and {@code false} + * otherwise. The latter usually means that the zygote eagerly preloaded at startup + * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous. + */ + public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException { + synchronized (mLock) { + ZygoteState state = openZygoteSocketIfNeeded(abi); + // Each query starts with the argument count (1 in this case) + state.writer.write("1"); + state.writer.newLine(); + state.writer.write("--preload-default"); + state.writer.newLine(); + state.writer.flush(); + + return (state.inputStream.readInt() == 0); + } + } } diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java index f242d7936776..3dfedea18323 100644 --- a/core/java/android/provider/SettingsStringUtil.java +++ b/core/java/android/provider/SettingsStringUtil.java @@ -60,7 +60,7 @@ public class SettingsStringUtil { StringBuilder sb = new StringBuilder(); Iterator<T> it = iterator(); if (it.hasNext()) { - sb.append(it.next()); + sb.append(itemToString(it.next())); while (it.hasNext()) { sb.append(DELIMITER); sb.append(itemToString(it.next())); diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java index cb5f2209fa37..491eabc91029 100644 --- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java +++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java @@ -97,6 +97,8 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem } mAudioTrack.setPlaybackPositionUpdateListener(this); + // Ensure we set the first marker if there is one. + updateMarker(); try { byte[] buffer = null; diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java index 24c119f29d71..31ed549260a0 100644 --- a/core/java/android/text/method/LinkMovementMethod.java +++ b/core/java/android/text/method/LinkMovementMethod.java @@ -29,9 +29,6 @@ import android.widget.TextView; /** * A movement method that traverses links in the text buffer and scrolls if necessary. * Supports clicking on links with DPad Center or Enter. - * - * <p>Note: Starting from Android 8.0 (API level 25) this class no longer handles the touch - * clicks. */ public class LinkMovementMethod extends ScrollingMovementMethod { private static final int CLICK = 1; @@ -198,7 +195,7 @@ public class LinkMovementMethod extends ScrollingMovementMethod { MotionEvent event) { int action = event.getAction(); - if (action == MotionEvent.ACTION_DOWN) { + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { int x = (int) event.getX(); int y = (int) event.getY(); @@ -215,9 +212,13 @@ public class LinkMovementMethod extends ScrollingMovementMethod { ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class); if (links.length != 0) { - Selection.setSelection(buffer, + if (action == MotionEvent.ACTION_UP) { + links[0].onClick(widget); + } else if (action == MotionEvent.ACTION_DOWN) { + Selection.setSelection(buffer, buffer.getSpanStart(links[0]), buffer.getSpanEnd(links[0])); + } return true; } else { Selection.removeSelection(buffer); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 17cd446c3d60..5572cbbcfc73 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -121,7 +121,6 @@ import android.view.ActionMode; import android.view.Choreographer; import android.view.ContextMenu; import android.view.DragEvent; -import android.view.GestureDetector; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyCharacterMap; @@ -682,8 +681,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private Editor mEditor; - private GestureDetector mClickableSpanOnClickGestureDetector; - private static final int DEVICE_PROVISIONED_UNKNOWN = 0; private static final int DEVICE_PROVISIONED_NO = 1; private static final int DEVICE_PROVISIONED_YES = 2; @@ -9319,24 +9316,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } - // Lazily create the clickable span gesture detector only if it looks like it - // might be useful. - if (action == MotionEvent.ACTION_DOWN && mClickableSpanOnClickGestureDetector == null - && shouldUseClickableSpanOnClickGestureDetector()) { - ClickableSpan[] links = ((Spannable) mText).getSpans( - getSelectionStart(), getSelectionEnd(), - ClickableSpan.class); + final boolean textIsSelectable = isTextSelectable(); + if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) { + // The LinkMovementMethod which should handle taps on links has not been installed + // on non editable text that support text selection. + // We reproduce its behavior here to open links for these. + ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(), + getSelectionEnd(), ClickableSpan.class); + if (links.length > 0) { - mClickableSpanOnClickGestureDetector = - createClickableSpanOnClickGestureDetector(); + links[0].onClick(this); + handled = true; } } - if (mClickableSpanOnClickGestureDetector != null) { - handled |= mClickableSpanOnClickGestureDetector.onTouchEvent(event); - } - - if (touchIsFinished && (isTextEditable() || isTextSelectable())) { + if (touchIsFinished && (isTextEditable() || textIsSelectable)) { // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); @@ -9754,31 +9748,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.onLocaleChanged(); } - private GestureDetector createClickableSpanOnClickGestureDetector() { - return new GestureDetector(mContext, - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onSingleTapUp(MotionEvent e) { - if (shouldUseClickableSpanOnClickGestureDetector()) { - ClickableSpan[] links = ((Spannable) mText).getSpans( - getSelectionStart(), getSelectionEnd(), - ClickableSpan.class); - if (links.length > 0) { - links[0].onClick(TextView.this); - return true; - } - } - return false; - } - }); - } - - private boolean shouldUseClickableSpanOnClickGestureDetector() { - return mLinksClickable && (mMovement != null) && - (mMovement instanceof LinkMovementMethod - || (mAutoLinkMask != 0 && isTextSelectable())); - } - /** * This method is used by the ArrowKeyMovementMethod to jump from one word to the other. * Made available to achieve a consistent behavior. diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index d82a211446d3..f27c0d4df4ce 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -55,8 +55,15 @@ class WebViewZygoteInit { } @Override - protected void maybePreload() { - // Do nothing, we don't need to call ZygoteInit.maybePreload() for the WebView zygote. + protected void preload() { + // Nothing to preload by default. + } + + @Override + protected boolean isPreloadComplete() { + // Webview zygotes don't preload any classes or resources or defaults, all of their + // preloading is package specific. + return true; } @Override diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 59416dd06cdc..fa71a628e3ca 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -173,6 +173,10 @@ public final class Zygote { VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet); } + /** + * Resets this process' priority to the default value (0). + */ + native static void nativeResetNicePriority(); /** * Executes "/system/bin/sh -c <command>" using the exec() system call. diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index a7f311b33963..e2485e90e537 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -171,7 +171,9 @@ class ZygoteConnection { return handleAbiListQuery(); } - maybePreload(); + if (parsedArgs.preloadDefault) { + return handlePreload(); + } if (parsedArgs.preloadPackage != null) { return handlePreloadPackage(parsedArgs.preloadPackage, @@ -282,8 +284,34 @@ class ZygoteConnection { } } - protected void maybePreload() { - ZygoteInit.maybePreload(); + /** + * Preloads resources if the zygote is in lazily preload mode. Writes the result of the + * preload operation; {@code 0} when a preload was initiated due to this request and {@code 1} + * if no preload was initiated. The latter implies that the zygote is not configured to load + * resources lazy or that the zygote has already handled a previous request to handlePreload. + */ + private boolean handlePreload() { + try { + if (isPreloadComplete()) { + mSocketOutStream.writeInt(1); + } else { + preload(); + mSocketOutStream.writeInt(0); + } + + return false; + } catch (IOException ioe) { + Log.e(TAG, "Error writing to command socket", ioe); + return true; + } + } + + protected void preload() { + ZygoteInit.lazyPreload(); + } + + protected boolean isPreloadComplete() { + return ZygoteInit.isPreloadComplete(); } protected boolean handlePreloadPackage(String packagePath, String libsPath) { @@ -402,6 +430,13 @@ class ZygoteConnection { String preloadPackageLibs; /** + * Whether this is a request to start preloading the default resources and classes. + * This argument only makes sense when the zygote is in lazy preload mode (i.e, when + * it's started with --enable-lazy-preload). + */ + boolean preloadDefault; + + /** * Constructs instance and parses args * @param args zygote command-line args * @throws IllegalArgumentException @@ -564,6 +599,8 @@ class ZygoteConnection { } else if (arg.equals("--preload-package")) { preloadPackage = args[++curArg]; preloadPackageLibs = args[++curArg]; + } else if (arg.equals("--preload-default")) { + preloadDefault = true; } else { break; } @@ -578,7 +615,7 @@ class ZygoteConnection { throw new IllegalArgumentException( "Unexpected arguments after --preload-package."); } - } else { + } else if (!preloadDefault) { if (!seenRuntimeArgs) { throw new IllegalArgumentException("Unexpected argument : " + args[curArg]); } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index a72b66a6f255..0b5a1b72f9ea 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -51,6 +51,7 @@ import android.widget.TextView; import com.android.internal.logging.MetricsLogger; +import com.android.internal.util.Preconditions; import dalvik.system.DexFile; import dalvik.system.PathClassLoader; import dalvik.system.VMRuntime; @@ -146,11 +147,11 @@ public class ZygoteInit { sPreloadComplete = true; } - public static void maybePreload() { - if (!sPreloadComplete) { - Log.i(TAG, "Lazily preloading resources."); - preload(new BootTimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK)); - } + public static void lazyPreload() { + Preconditions.checkState(!sPreloadComplete); + Log.i(TAG, "Lazily preloading resources."); + + preload(new BootTimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK)); } private static void beginIcuCachePinning() { @@ -712,6 +713,8 @@ public class ZygoteInit { EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload + } else { + Zygote.nativeResetNicePriority(); } // Finish profiling the zygote initialization. @@ -783,6 +786,10 @@ public class ZygoteInit { } } + static boolean isPreloadComplete() { + return sPreloadComplete; + } + /** * Class not instantiable. */ diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 261fa4327494..6d814bf14bc0 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -79,7 +79,6 @@ public class SwipeDismissLayout extends FrameLayout { private boolean mDismissed; private boolean mDiscardIntercept; private VelocityTracker mVelocityTracker; - private float mTranslationX; private boolean mBlockGesture = false; private boolean mActivityTranslucencyConverted = false; @@ -166,8 +165,10 @@ public class SwipeDismissLayout extends FrameLayout { return super.onInterceptTouchEvent(ev); } - // offset because the view is translated during swipe - ev.offsetLocation(mTranslationX, 0); + // Offset because the view is translated during swipe, match X with raw X. Active touch + // coordinates are mostly used by the velocity tracker, so offset it to match the raw + // coordinates which is what is primarily used elsewhere. + ev.offsetLocation(ev.getRawX() - ev.getX(), 0); switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: @@ -232,8 +233,12 @@ public class SwipeDismissLayout extends FrameLayout { if (mVelocityTracker == null || !mDismissable) { return super.onTouchEvent(ev); } - // offset because the view is translated during swipe - ev.offsetLocation(mTranslationX, 0); + + // Offset because the view is translated during swipe, match X with raw X. Active touch + // coordinates are mostly used by the velocity tracker, so offset it to match the raw + // coordinates which is what is primarily used elsewhere. + ev.offsetLocation(ev.getRawX() - ev.getX(), 0); + switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: updateDismiss(ev); @@ -266,7 +271,6 @@ public class SwipeDismissLayout extends FrameLayout { } private void setProgress(float deltaX) { - mTranslationX = deltaX; if (mProgressListener != null && deltaX >= 0) { mProgressListener.onSwipeProgressChanged( this, progressToAlpha(deltaX / getWidth()), deltaX); @@ -300,7 +304,6 @@ public class SwipeDismissLayout extends FrameLayout { mVelocityTracker.recycle(); } mVelocityTracker = null; - mTranslationX = 0; mDownX = 0; mLastX = Integer.MIN_VALUE; mDownY = 0; diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index c49287ccddb2..c261e414203c 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -27,6 +27,7 @@ #include "Bitmap.h" #include "SkDrawFilter.h" #include "SkGraphics.h" +#include "SkRegion.h" namespace android { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index e2fc44460dcd..c3f0e9d81944 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -806,6 +806,10 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en UnmountTree("/storage"); } +static void com_android_internal_os_Zygote_nativeResetNicePriority(JNIEnv* env, jclass) { + ResetNicePriority(env); +} + static const JNINativeMethod gMethods[] = { { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I", @@ -815,7 +819,9 @@ static const JNINativeMethod gMethods[] = { { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V", (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork }, { "nativeUnmountStorageOnInit", "()V", - (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit } + (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }, + { "nativeResetNicePriority", "()V", + (void *) com_android_internal_os_Zygote_nativeResetNicePriority } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 36bb821bbc41..4d5e45bae1c3 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5846,8 +5846,6 @@ <!-- Drawable used to draw masked icons with foreground and background layers. --> <declare-styleable name="MaskableIconDrawableLayer"> - <!-- The color to use for the layer, only if drawable is not defined. --> - <attr name="color" /> <!-- The drawable to use for the layer. --> <attr name="drawable" /> </declare-styleable> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 54c392f6eb2d..72a2e43bc8c8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2541,7 +2541,7 @@ <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. These values are in DPs and will be converted to pixel sizes internally. --> - <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">8x8</string> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string> <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside. These values are in DPs and will be converted to pixel sizes internally. --> diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk new file mode 100644 index 000000000000..c1e8c981579c --- /dev/null +++ b/core/tests/packagemanagertests/Android.mk @@ -0,0 +1,20 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + frameworks-base-testutils + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests + +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/core/tests/packagemanagertests/AndroidManifest.xml b/core/tests/packagemanagertests/AndroidManifest.xml new file mode 100644 index 000000000000..8f490083b16e --- /dev/null +++ b/core/tests/packagemanagertests/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + android:installLocation="internalOnly" + package="com.android.frameworks.coretests.packagemanager" + android:sharedUserId="android.uid.system"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.coretests.packagemanager" + android:label="Frameworks PackageManager Core Tests" /> + +</manifest> diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java new file mode 100644 index 000000000000..1097bc731de8 --- /dev/null +++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java @@ -0,0 +1,109 @@ +/* + * 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 android.content.pm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.os.FileUtils; +import android.os.Process; +import android.os.ServiceManager; +import android.os.UserManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +/** + * This test needs to be run without any secondary users on the device, + * and selinux needs to be disabled with "adb shell setenforce 0". + */ +@RunWith(AndroidJUnit4.class) +public class KernelPackageMappingTests { + + private static final String TAG = "KernelPackageMapping"; + private static final String SDCARDFS_PATH = "/config/sdcardfs"; + + private UserInfo mSecondaryUser; + + private static File getKernelPackageDir(String packageName) { + return new File(new File(SDCARDFS_PATH), packageName); + } + + private static File getKernelPackageFile(String packageName, String filename) { + return new File(getKernelPackageDir(packageName), filename); + } + + private UserManager getUserManager() { + UserManager um = (UserManager) InstrumentationRegistry.getContext().getSystemService( + Context.USER_SERVICE); + return um; + } + + private IPackageManager getIPackageManager() { + IPackageManager ipm = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + return ipm; + } + + private static String getContent(File file) { + try { + return FileUtils.readTextFile(file, 0, null).trim(); + } catch (IOException ioe) { + Log.w(TAG, "Couldn't read file " + file.getAbsolutePath() + "\n" + ioe); + return "<error>"; + } + } + + @Test + public void testInstalledPrimary() throws Exception { + assertEquals("1000", getContent(getKernelPackageFile("com.android.settings", "appid"))); + } + + @Test + public void testInstalledAll() throws Exception { + assertEquals("", getContent(getKernelPackageFile("com.android.settings", + "excluded_userids"))); + } + + @Test + public void testNotInstalledSecondary() throws Exception { + mSecondaryUser = getUserManager().createUser("Secondary", 0); + assertEquals(Integer.toString(mSecondaryUser.id), + getContent( + getKernelPackageFile("com.android.frameworks.coretests.packagemanager", + "excluded_userids"))); + } + + @After + public void shutDown() throws Exception { + if (mSecondaryUser != null) { + getUserManager().removeUser(mSecondaryUser.id); + } + } +} diff --git a/graphics/java/android/graphics/drawable/MaskableIconDrawable.java b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java index e4f1788a17eb..472b22907823 100644 --- a/graphics/java/android/graphics/drawable/MaskableIconDrawable.java +++ b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java @@ -427,7 +427,7 @@ public class MaskableIconDrawable extends Drawable implements Drawable.Callback } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException(parser.getPositionDescription() - + ": <foreground> or <background> tag requires a 'color' or 'drawable'" + + ": <foreground> or <background> tag requires a 'drawable'" + "attribute or child tag defining a drawable"); } @@ -451,12 +451,6 @@ public class MaskableIconDrawable extends Drawable implements Drawable.Callback layer.mThemeAttrs = a.extractThemeAttrs(); Drawable dr = a.getDrawable(R.styleable.MaskableIconDrawableLayer_drawable); - if (dr == null) { - int color = a.getColor(R.styleable.MaskableIconDrawableLayer_color, Color.TRANSPARENT); - if (color != Color.TRANSPARENT) { - dr = new ColorDrawable(color); - } - } if (dr != null) { if (layer.mDrawable != null) { // It's possible that a drawable was already set, in which case diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index 9701b0ea9308..988e32cff069 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -200,6 +200,11 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { + if (mKeySizeBits < 64) { + throw new InvalidAlgorithmParameterException( + "HMAC key size must be at least 64 bits."); + } + // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm // implies SHA-256 digest). Because keymaster HMAC key is authorized only for // one digest, we don't let algorithm parameter spec override the digest implied diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index fb3f5b38959b..a4f2a7ea49f0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1456,10 +1456,11 @@ public class AudioManager { } /** - * Checks whether A2DP audio routing to the Bluetooth headset is on or off. + * Checks whether a Bluetooth A2DP audio peripheral is connected or not. * - * @return true if A2DP audio is being routed to/from Bluetooth headset; + * @return true if a Bluetooth A2DP peripheral is connected * false if otherwise + * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices. */ public boolean isBluetoothA2dpOn() { if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"") @@ -1492,7 +1493,7 @@ public class AudioManager { * * @return true if a wired headset is connected. * false if otherwise - * @deprecated Use only to check is a headset is connected or not. + * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices. */ public boolean isWiredHeadsetOn() { if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,"") diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 561d92447c1f..1f03b51aef9e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -1504,4 +1504,20 @@ public class ApplicationsState { return isMusicApp; } }; + + public static final AppFilter FILTER_OTHER_APPS = new AppFilter() { + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + boolean isCategorized; + synchronized(entry) { + isCategorized = entry.info.category == ApplicationInfo.CATEGORY_AUDIO || + entry.info.category == ApplicationInfo.CATEGORY_GAME; + } + return !isCategorized; + } + }; } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java index 8a6ae86e4156..80e1cbf45357 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java @@ -86,4 +86,25 @@ public class ApplicationsStateTest { assertThat(ApplicationsState.FILTER_AUDIO.filterApp(mEntry)).isFalse(); } + + @Test + public void testOtherAppsRejectsAudio() { + mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO; + + assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse(); + } + + @Test + public void testOtherAppsRejectsGame() { + mEntry.info.category = ApplicationInfo.CATEGORY_GAME; + + assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isFalse(); + } + + @Test + public void testOtherAppsAcceptsDefaultCategory() { + mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED; + + assertThat(ApplicationsState.FILTER_OTHER_APPS.filterApp(mEntry)).isTrue(); + } } diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml new file mode 100644 index 000000000000..d6abc47c5af3 --- /dev/null +++ b/packages/SystemUI/res/layout/battery_percentage_view.xml @@ -0,0 +1,29 @@ +<?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 + --> + +<!-- Loaded into BatteryMeterView as necessary --> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/battery_percentage_view" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" + android:textColor="?android:attr/textColorPrimary" + android:gravity="center_vertical|start" + android:paddingEnd="4dp" + /> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index 3a339920618d..bfa92ad89a1e 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -31,9 +31,8 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/signal_cluster_margin_start"/> - <!-- battery must be padded below to match assets --> <com.android.systemui.BatteryMeterView android:id="@+id/battery" - android:layout_height="@dimen/status_bar_battery_icon_height" - android:layout_width="@dimen/status_bar_battery_icon_width" - android:layout_marginBottom="@dimen/battery_margin_bottom"/> + android:layout_height="match_parent" + android:layout_width="wrap_content" + /> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e737d2d6f007..2102e0712015 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -69,8 +69,8 @@ <!-- Height of a small notification in the status bar--> <dimen name="notification_min_height">92dp</dimen> - <!-- Height of a small notification in the status bar if it is a large (like messaging)--> - <dimen name="notification_min_height_large">132dp</dimen> + <!-- Increased height of a small notification in the status bar --> + <dimen name="notification_min_height_increased">132dp</dimen> <!-- Height of a small notification in the status bar which was used before android N --> <dimen name="notification_min_height_legacy">64dp</dimen> @@ -87,6 +87,9 @@ <!-- Height of a heads up notification in the status bar --> <dimen name="notification_max_heads_up_height">148dp</dimen> + <!-- Height of a heads up notification in the status bar --> + <dimen name="notification_max_heads_up_height_increased">188dp</dimen> + <!-- a threshold in dp per second that is considered fast scrolling --> <dimen name="scroll_fast_threshold">1500dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java deleted file mode 100644 index 9068079fd6cf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui; - -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.provider.Settings; -import com.android.settingslib.graph.BatteryMeterDrawableBase; -import com.android.systemui.statusbar.policy.BatteryController; - -public class BatteryMeterDrawable extends BatteryMeterDrawableBase implements - BatteryController.BatteryStateChangeCallback { - - public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; - - private BatteryController mBatteryController; - private SettingObserver mSettingObserver; - - public BatteryMeterDrawable(Context context, int frameColor) { - super(context, frameColor); - - mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper())); - } - - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - setBatteryLevel(level); - setPluggedIn(pluggedIn); - } - - @Override - public void onPowerSaveChanged(boolean isPowerSave) { - setPowerSave(isPowerSave); - } - - public void startListening() { - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); - updateShowPercent(); - mBatteryController.addCallback(this); - } - - public void stopListening() { - mContext.getContentResolver().unregisterContentObserver(mSettingObserver); - mBatteryController.removeCallback(this); - } - - protected void updateShowPercent() { - setShowPercent(0 != Settings.System.getInt(mContext.getContentResolver(), - SHOW_PERCENT_SETTING, 0)); - } - - public void setBatteryController(BatteryController batteryController) { - mBatteryController = batteryController; - setPowerSave(mBatteryController.isPowerSave()); - } - - private final class SettingObserver extends ContentObserver { - public SettingObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - updateShowPercent(); - postInvalidate(); - } - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 69e38745f23b..f82130868554 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -23,10 +23,22 @@ import android.graphics.Rect; import android.util.ArraySet; import android.util.AttributeSet; import android.util.TypedValue; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -37,12 +49,22 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; -public class BatteryMeterView extends ImageView implements +import java.text.NumberFormat; + +public class BatteryMeterView extends LinearLayout implements BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener { - private final BatteryMeterDrawable mDrawable; + public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent"; + + private final BatteryMeterDrawableBase mDrawable; private final String mSlotBattery; + private final ImageView mBatteryIconView; + private TextView mBatteryPercentView; + private BatteryController mBatteryController; + private SettingObserver mSettingObserver; + private int mTextColor; + private int mLevel; public BatteryMeterView(Context context) { this(context, null, 0); @@ -55,16 +77,35 @@ public class BatteryMeterView extends ImageView implements public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.CENTER_VERTICAL | Gravity.START); + TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView, defStyle, 0); final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor, context.getColor(R.color.batterymeter_frame_color)); - mDrawable = new BatteryMeterDrawable(context, frameColor); + mDrawable = new BatteryMeterDrawableBase(context, frameColor); atts.recycle(); + mSettingObserver = new SettingObserver(new Handler(context.getMainLooper())); + mSlotBattery = context.getString( com.android.internal.R.string.status_bar_battery); - setImageDrawable(mDrawable); + mBatteryIconView = new ImageView(context); + mBatteryIconView.setImageDrawable(mDrawable); + final MarginLayoutParams mlp = new MarginLayoutParams( + getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width), + getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height)); + mlp.setMargins(0, 0, 0, + getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom)); + addView(mBatteryIconView, mlp); + + updateShowPercent(); + } + + // StatusBarIconController reaches in here and adjusts the layout parameters of the icon + public ImageView getBatteryIconView() { + return mBatteryIconView; } @Override @@ -84,9 +125,10 @@ public class BatteryMeterView extends ImageView implements public void onAttachedToWindow() { super.onAttachedToWindow(); mBatteryController = Dependency.get(BatteryController.class); - mDrawable.setBatteryController(mBatteryController); mBatteryController.addCallback(this); - mDrawable.startListening(); + getContext().getContentResolver().registerContentObserver( + Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver); + updateShowPercent(); Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); Dependency.get(ConfigurationController.class).addCallback(this); } @@ -95,13 +137,17 @@ public class BatteryMeterView extends ImageView implements public void onDetachedFromWindow() { super.onDetachedFromWindow(); mBatteryController.removeCallback(this); - mDrawable.stopListening(); + getContext().getContentResolver().unregisterContentObserver(mSettingObserver); Dependency.get(TunerService.class).removeTunable(this); Dependency.get(ConfigurationController.class).removeCallback(this); } @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + mDrawable.setBatteryLevel(level); + mDrawable.setPluggedIn(pluggedIn); + mLevel = level; + updatePercentText(); setContentDescription( getContext().getString(charging ? R.string.accessibility_battery_level_charging : R.string.accessibility_battery_level, level)); @@ -109,7 +155,41 @@ public class BatteryMeterView extends ImageView implements @Override public void onPowerSaveChanged(boolean isPowerSave) { + mDrawable.setPowerSave(isPowerSave); + } + private TextView loadPercentView() { + return (TextView) LayoutInflater.from(getContext()) + .inflate(R.layout.battery_percentage_view, null); + } + + private void updatePercentText() { + if (mBatteryPercentView != null) { + mBatteryPercentView.setText( + NumberFormat.getPercentInstance().format(mLevel/100f)); + } + } + + private void updateShowPercent() { + final boolean showing = mBatteryPercentView != null; + if (0 != Settings.System.getInt(getContext().getContentResolver(), + BatteryMeterView.SHOW_PERCENT_SETTING, 0)) { + if (!showing) { + mBatteryPercentView = loadPercentView(); + if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); + updatePercentText(); + addView(mBatteryPercentView, + 0, + new ViewGroup.LayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.MATCH_PARENT)); + } + } else { + if (showing) { + removeView(mBatteryPercentView); + mBatteryPercentView = null; + } + } } @Override @@ -133,17 +213,37 @@ public class BatteryMeterView extends ImageView implements LinearLayout.LayoutParams scaledLayoutParams = new LinearLayout.LayoutParams( (int) (batteryWidth * iconScaleFactor), (int) (batteryHeight * iconScaleFactor)); - scaledLayoutParams.setMarginsRelative(0, 0, 0, marginBottom); + scaledLayoutParams.setMargins(0, 0, 0, marginBottom); - setLayoutParams(scaledLayoutParams); + mBatteryIconView.setLayoutParams(scaledLayoutParams); } @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { mDrawable.setDarkIntensity(DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0); + setTextColor(DarkIconDispatcher.getTint(area, this, tint)); + } + + public void setTextColor(int color) { + mTextColor = color; + if (mBatteryPercentView != null) { + mBatteryPercentView.setTextColor(color); + } } public void setRawColors(int fgColor, int bgColor) { mDrawable.setColors(fgColor, bgColor); } + + private final class SettingObserver extends ContentObserver { + public SettingObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + updateShowPercent(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java index 23eaed9007ac..32b5862e2b6b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java @@ -17,7 +17,6 @@ package com.android.systemui.keyguard; import static android.app.ActivityManager.TaskDescription; -import static android.app.ActivityManager.StackId; import android.annotation.ColorInt; import android.annotation.UserIdInt; @@ -32,13 +31,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; -import android.graphics.Rect; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.View; +import com.android.internal.annotations.VisibleForTesting; + /** * Bouncer between work activities and the activity used to confirm credentials before unlocking * a managed profile. @@ -51,52 +51,39 @@ public class WorkLockActivity extends Activity { private static final String TAG = "WorkLockActivity"; /** - * ID of the locked user that this activity blocks access to. + * Contains a {@link TaskDescription} for the activity being covered. */ - @UserIdInt - private int mUserId; - + static final String EXTRA_TASK_DESCRIPTION = + "com.android.systemui.keyguard.extra.TASK_DESCRIPTION"; + /** - * {@see KeyguardManager} + * Cached keyguard manager instance populated by {@link #getKeyguardManager}. + * @see KeyguardManager */ private KeyguardManager mKgm; - /** - * {@see DevicePolicyManager} - */ - private DevicePolicyManager mDpm; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); - mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); - mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); - - final IntentFilter lockFilter = new IntentFilter(); - lockFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED); - registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, lockFilter, - /* permission */ null, /* scheduler */ null); + registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, + new IntentFilter(Intent.ACTION_DEVICE_LOCKED_CHANGED), /* permission */ null, + /* scheduler */ null); // Once the receiver is registered, check whether anything happened between now and the time // when this activity was launched. If it did and the user is unlocked now, just quit. - if (!mKgm.isDeviceLocked(mUserId)) { + if (!getKeyguardManager().isDeviceLocked(getTargetUserId())) { finish(); return; } - // Get the organization color; this is a 24-bit integer provided by a DPC, guaranteed to - // be completely opaque. - final @ColorInt int color = mDpm.getOrganizationColorForUser(mUserId); - // Draw captions overlaid on the content view, so the whole window is one solid color. setOverlayWithDecorCaptionEnabled(true); // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with // redaction switched on. final View blankView = new View(this); - blankView.setBackgroundColor(color); + blankView.setBackgroundColor(getPrimaryColor()); setContentView(blankView); } @@ -127,26 +114,28 @@ public class WorkLockActivity extends Activity { @Override public void setTaskDescription(TaskDescription taskDescription) { - // Use the previous activity's task description. + // Leave unset so we use the previous activity's task description. } private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, mUserId); - if (userId == mUserId && !mKgm.isDeviceLocked(mUserId)) { + final int targetUserId = getTargetUserId(); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, targetUserId); + if (userId == targetUserId && !getKeyguardManager().isDeviceLocked(targetUserId)) { finish(); } } }; private void showConfirmCredentialActivity() { - if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) { + if (isFinishing() || !getKeyguardManager().isDeviceLocked(getTargetUserId())) { // Don't show the confirm credentials screen if we are already unlocked / unlocking. return; } - final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId); + final Intent credential = getKeyguardManager() + .createConfirmDeviceCredentialIntent(null, null, getTargetUserId()); if (credential == null) { return; } @@ -181,4 +170,32 @@ public class WorkLockActivity extends Activity { final View view = getWindow().getDecorView(); return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight()); } + + private KeyguardManager getKeyguardManager() { + if (mKgm == null) { + mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + } + return mKgm; + } + + @VisibleForTesting + @UserIdInt + final int getTargetUserId() { + return getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); + } + + @VisibleForTesting + @ColorInt + final int getPrimaryColor() { + final TaskDescription taskDescription = (TaskDescription) + getIntent().getExtra(EXTRA_TASK_DESCRIPTION); + if (taskDescription != null && Color.alpha(taskDescription.getPrimaryColor()) == 255) { + return taskDescription.getPrimaryColor(); + } else { + // No task description. Use an organization color set by the policy controller. + final DevicePolicyManager devicePolicyManager = (DevicePolicyManager) + getSystemService(Context.DEVICE_POLICY_SERVICE); + return devicePolicyManager.getOrganizationColorForUser(getTargetUserId()); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java index e6483f6332ba..a49c4820aafd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -31,17 +31,21 @@ import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; public class WorkLockActivityController { private final Context mContext; + final SystemServicesProxy mSsp; public WorkLockActivityController(Context context) { mContext = context; + mSsp = SystemServicesProxy.getInstance(context); + EventBus.getDefault().register(this); - SystemServicesProxy.getInstance(context).registerTaskStackListener(mLockListener); + mSsp.registerTaskStackListener(mLockListener); } private void startWorkChallengeInTask(int taskId, int userId) { Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER) .setComponent(new ComponentName(mContext, WorkLockActivity.class)) .putExtra(Intent.EXTRA_USER_ID, userId) + .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, mSsp.getTaskDescription(taskId)) .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java index fff830560c14..8227f8f63ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java @@ -35,8 +35,8 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.BatteryInfo; +import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.settingslib.graph.UsageView; -import com.android.systemui.BatteryMeterDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS.DetailAdapter; @@ -155,8 +155,10 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll private final class BatteryDetail implements DetailAdapter, OnClickListener, OnAttachStateChangeListener { - private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(), - mHost.getContext().getColor(R.color.batterymeter_frame_color)); + private final BatteryMeterDrawableBase mDrawable + = new BatteryMeterDrawableBase( + mHost.getContext(), + mHost.getContext().getColor(R.color.batterymeter_frame_color)); private View mCurrentView; @Override @@ -195,8 +197,9 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll if (mCurrentView == null) { return; } - mDrawable.onBatteryLevelChanged(100, false, false); - mDrawable.onPowerSaveChanged(true); + mDrawable.setBatteryLevel(100); + mDrawable.setPluggedIn(false); + mDrawable.setPowerSave(true); mDrawable.setShowPercent(false); ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable); Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 55491b24e20d..8de4e5837a6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -130,6 +130,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener launchOpts.numVisibleTaskThumbnails = 2; launchOpts.onlyLoadForCache = true; launchOpts.onlyLoadPausedActivities = true; + launchOpts.loadThumbnails = !ActivityManager.ENABLE_TASK_SNAPSHOTS; loader.loadTasks(mContext, plan, launchOpts); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 49074a6f535d..eae1b8155486 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -868,6 +868,14 @@ public class SystemServicesProxy { return null; } + public ActivityManager.TaskDescription getTaskDescription(int taskId) { + try { + return mIam.getTaskDescription(taskId); + } catch (RemoteException e) { + return null; + } + } + /** * Returns the given icon for a user, badging if necessary. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 0160eb75773e..40aad4573354 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -20,6 +20,8 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; @@ -1496,11 +1498,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Remove the task from the ignored set removeIgnoreTask(removedTask); - // Resize the grid layout task view focus frame - if (mTaskViewFocusFrame != null) { - mTaskViewFocusFrame.resize(); - } - // If requested, relayout with the given animation if (animation != null) { updateLayoutAlgorithm(true /* boundScroll */); @@ -1838,6 +1835,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal announceForAccessibility(getContext().getString( R.string.accessibility_recents_item_dismissed, event.task.title)); + if (useGridLayout() && event.animation != null) { + event.animation.setListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { + if (mTaskViewFocusFrame != null) { + // Resize the grid layout task view focus frame + mTaskViewFocusFrame.resize(); + } + } + }); + } + // Remove the task from the stack mStack.removeTask(event.task, event.animation, false /* fromDockGesture */); EventBus.getDefault().send(new DeleteTaskDataEvent(event.task)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 9a52a7b7d5c1..a5f78322fca0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -270,7 +270,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks return super.onInterceptTouchEvent(ev); } - @Override protected void measureContents(int width, int height) { int widthWithoutPadding = width - mPaddingLeft - mPaddingRight; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 4ac0f9eed519..02b01136f352 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -77,10 +77,11 @@ public class TaskViewThumbnail extends View { @ViewDebug.ExportedProperty(category="recents") private float mDimAlpha; private Matrix mMatrix = new Matrix(); - protected Paint mDrawPaint = new Paint(); - private Paint mLockedPaint = new Paint(); + private Paint mDrawPaint = new Paint(); + protected Paint mLockedPaint = new Paint(); protected Paint mBgFillPaint = new Paint(); protected BitmapShader mBitmapShader; + protected boolean mUserLocked = false; private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0); // Clip the top of the thumbnail against the opaque header bar that overlaps this view @@ -152,7 +153,7 @@ public class TaskViewThumbnail extends View { int thumbnailHeight = Math.min(viewHeight, (int) (mThumbnailRect.height() * mThumbnailScale)); - if (mTask != null && mTask.isLocked) { + if (mUserLocked) { canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius, mLockedPaint); } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { @@ -344,6 +345,17 @@ public class TaskViewThumbnail extends View { } /** + * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the + * thumbnail shouldn't be drawn because it belongs to a locked user. + */ + protected Paint getDrawPaint() { + if (mUserLocked) { + return mLockedPaint; + } + return mDrawPaint; + } + + /** * Binds the thumbnail view to the task. */ void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) { @@ -354,7 +366,10 @@ public class TaskViewThumbnail extends View { if (t.colorBackground != 0) { mBgFillPaint.setColor(t.colorBackground); } - mLockedPaint.setColor(t.colorPrimary); + if (t.colorPrimary != 0) { + mLockedPaint.setColor(t.colorPrimary); + } + mUserLocked = t.isLocked; EventBus.getDefault().register(this); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java index 2c3e42b6c7d7..bcf4f17d4340 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java @@ -28,7 +28,6 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { private Path mThumbnailOutline; private Path mRestBackgroundOutline; - private Path mFullBackgroundOutline; // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should // be updated. private boolean mUpdateThumbnailOutline = true; @@ -145,10 +144,7 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { 90, 90, false); // F mRestBackgroundOutline.lineTo(l, t); // A mRestBackgroundOutline.close(); - } - } else { - mFullBackgroundOutline = mThumbnailOutline; } } @@ -167,7 +163,10 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { updateThumbnailOutline(); mUpdateThumbnailOutline = false; } - if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { + + if (mUserLocked) { + canvas.drawPath(mThumbnailOutline, mLockedPaint); + } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { // Draw the background, there will be some small overdraw with the thumbnail if (thumbnailWidth < viewWidth) { // Portrait thumbnail on a landscape task view @@ -177,9 +176,9 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { // Landscape thumbnail on a portrait task view canvas.drawPath(mRestBackgroundOutline, mBgFillPaint); } - canvas.drawPath(mThumbnailOutline, mDrawPaint); + canvas.drawPath(mThumbnailOutline, getDrawPaint()); } else { - canvas.drawPath(mFullBackgroundOutline, mBgFillPaint); + canvas.drawPath(mThumbnailOutline, mBgFillPaint); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 5366da112275..995901b467b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -164,15 +164,19 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void disable(int state1, int state2) { + public void disable(int state1, int state2, boolean animate) { synchronized (mLock) { mDisable1 = state1; mDisable2 = state2; mHandler.removeMessages(MSG_DISABLE); - mHandler.obtainMessage(MSG_DISABLE, state1, state2, null).sendToTarget(); + mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate).sendToTarget(); } } + public void disable(int state1, int state2) { + disable(state1, state2, true); + } + public void animateExpandNotificationsPanel() { synchronized (mLock) { mHandler.removeMessages(MSG_EXPAND_NOTIFICATIONS); @@ -433,7 +437,7 @@ public class CommandQueue extends IStatusBar.Stub { } case MSG_DISABLE: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).disable(msg.arg1, msg.arg2, true /* animate */); + mCallbacks.get(i).disable(msg.arg1, msg.arg2, (Boolean) msg.obj); } break; case MSG_EXPAND_NOTIFICATIONS: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 2e9c7fd85e42..3bbaf998cc9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -76,6 +76,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private int mNotificationMinHeightLegacy; private int mMaxHeadsUpHeightLegacy; private int mMaxHeadsUpHeight; + private int mMaxHeadsUpHeightIncreased; private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMaxHeight; @@ -209,6 +210,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private boolean mIsLowPriority; private boolean mIsColorized; private boolean mUseIncreasedCollapsedHeight; + private boolean mUseIncreasedHeadsUpHeight; + private float mTranslationWhenRemoved; + private boolean mWasChildInGroupWhenRemoved; @Override public boolean isGroupExpansionChanging() { @@ -354,8 +358,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { boolean headsUpCustom = layout.getHeadsUpChild() != null && layout.getHeadsUpChild().getId() != com.android.internal.R.id.status_bar_latest_event_content; - int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy - : mMaxHeadsUpHeight; + int headsUpheight; + if (headsUpCustom && beforeN) { + headsUpheight = mMaxHeadsUpHeightLegacy; + } else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) { + headsUpheight = mMaxHeadsUpHeightIncreased; + } else { + headsUpheight = mMaxHeadsUpHeight; + } layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight, mNotificationAmbientHeight); } @@ -828,10 +838,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { public void setRemoved() { mRemoved = true; - + mTranslationWhenRemoved = getTranslationY(); + mWasChildInGroupWhenRemoved = isChildInGroup(); + if (isChildInGroup()) { + mTranslationWhenRemoved += getNotificationParent().getTranslationY(); + } mPrivateLayout.setRemoved(); } + public boolean wasChildInGroupWhenRemoved() { + return mWasChildInGroupWhenRemoved; + } + + public float getTranslationWhenRemoved() { + return mTranslationWhenRemoved; + } + public NotificationChildrenContainer getChildrenContainer() { return mChildrenContainer; } @@ -991,6 +1013,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mUseIncreasedCollapsedHeight = use; } + public void setUseIncreasedHeadsUpHeight(boolean use) { + mUseIncreasedHeadsUpHeight = use; + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } @@ -1005,12 +1031,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy); mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height); mNotificationMinHeightLarge = getFontScaledHeight( - R.dimen.notification_min_height_large); + R.dimen.notification_min_height_increased); mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height); mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height); mMaxHeadsUpHeightLegacy = getFontScaledHeight( R.dimen.notification_max_heads_up_height_legacy); mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height); + mMaxHeadsUpHeightIncreased = getFontScaledHeight( + R.dimen.notification_max_heads_up_height_increased); mIncreasedPaddingBetweenElements = getResources() .getDimensionPixelSize(R.dimen.notification_divider_height_increased); mIconTransformContentShiftNoIcon = getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index bd5fb923cb84..0ea56b876d92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -381,12 +381,7 @@ public final class KeyboardShortcuts { if (appShortcuts != null) { result.add(appShortcuts); } - synchronized (sLock) { - // showKeyboardShortcutsDialog only if it has not been dismissed already - if (sInstance != null) { - showKeyboardShortcutsDialog(result); - } - } + showKeyboardShortcutsDialog(result); } }, deviceId); } @@ -585,7 +580,12 @@ public final class KeyboardShortcuts { mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); - mKeyboardShortcutsDialog.show(); + synchronized (sLock) { + // showKeyboardShortcutsDialog only if it has not been dismissed already + if (sInstance != null) { + mKeyboardShortcutsDialog.show(); + } + } } private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index f73a5ea04ae4..81db42927682 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -126,7 +126,8 @@ public class NotificationData { } public boolean cacheContentViews(Context ctx, Notification updatedNotification, - boolean isLowPriority, boolean useIncreasedCollapsedView) { + boolean isLowPriority, boolean useIncreasedCollapsedView, + boolean useIncreasedHeadsUp) { boolean applyInPlace = false; if (updatedNotification != null) { final Notification.Builder updatedNotificationBuilder @@ -136,7 +137,7 @@ public class NotificationData { final RemoteViews newBigContentView = createBigContentView( updatedNotificationBuilder, isLowPriority); final RemoteViews newHeadsUpContentView = - updatedNotificationBuilder.createHeadsUpContentView(); + updatedNotificationBuilder.createHeadsUpContentView(useIncreasedHeadsUp); final RemoteViews newPublicNotification = updatedNotificationBuilder.makePublicContentView(); final RemoteViews newAmbientNotification @@ -165,7 +166,7 @@ public class NotificationData { cachedContentView = createContentView(builder, isLowPriority, useIncreasedCollapsedView); cachedBigContentView = createBigContentView(builder, isLowPriority); - cachedHeadsUpContentView = builder.createHeadsUpContentView(); + cachedHeadsUpContentView = builder.createHeadsUpContentView(useIncreasedHeadsUp); cachedPublicContentView = builder.makePublicContentView(); cachedAmbientContentView = builder.makeAmbientNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index 1c89e32a02bc..535300573c6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java @@ -224,9 +224,6 @@ public class ViewTransformationHelper implements TransformableView { stack.push(viewRoot); while (!stack.isEmpty()) { View child = stack.pop(); - if (child.getVisibility() == View.GONE) { - continue; - } Boolean containsView = (Boolean) child.getTag(TAG_CONTAINS_TRANSFORMED_VIEW); if (containsView == null) { // This one is unhandled, let's add it to our list. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java index d15ab10e09ac..c3f1cb1c7a93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java @@ -69,7 +69,8 @@ public class TransformState { public void transformViewFrom(TransformState otherState, float transformationAmount) { mTransformedView.animate().cancel(); if (sameAs(otherState)) { - if (mTransformedView.getVisibility() == View.INVISIBLE) { + if (mTransformedView.getVisibility() == View.INVISIBLE + || mTransformedView.getAlpha() != 1.0f) { // We have the same content, lets show ourselves mTransformedView.setAlpha(1.0f); mTransformedView.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 91a71d1eb753..ac13cf4eb111 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -712,6 +712,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NetworkController mNetworkController; private KeyguardMonitorImpl mKeyguardMonitor; private BatteryController mBatteryController; + private boolean mPanelExpanded; private LogMaker mStatusBarStateLog; private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private NotificationIconAreaController mNotificationIconAreaController; @@ -838,7 +839,7 @@ public class StatusBar extends SystemUI implements DemoMode, createAndAddWindows(); mSettingsObserver.onChange(false); // set up - disable(switches[0], switches[6], false /* animate */); + mCommandQueue.disable(switches[0], switches[6], false /* animate */); setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff, fullscreenStackBounds, dockedStackBounds); topAppWindowChanged(switches[2] != 0); @@ -2510,7 +2511,7 @@ public class StatusBar extends SystemUI implements DemoMode, * This needs to be called if state used by {@link #adjustDisableFlags} changes. */ public void recomputeDisableFlags(boolean animate) { - disable(mDisabledUnmodified1, mDisabledUnmodified2, animate); + mCommandQueue.disable(mDisabledUnmodified1, mDisabledUnmodified2, animate); } protected H createHandler() { @@ -2679,6 +2680,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void setPanelExpanded(boolean isExpanded) { + mPanelExpanded = isExpanded; mStatusBarWindowManager.setPanelExpanded(isExpanded); mVisualStabilityManager.setPanelExpanded(isExpanded); if (isExpanded && getBarState() != StatusBarState.KEYGUARD) { @@ -6094,8 +6096,10 @@ public class StatusBar extends SystemUI implements DemoMode, boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn, mNotificationData.getImportance(sbn.getKey())); + boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded; try { - entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight); + entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight, + useIncreasedHeadsUp); } catch (RuntimeException e) { Log.e(TAG, "Unable to get notification remote views", e); return false; @@ -6266,6 +6270,7 @@ public class StatusBar extends SystemUI implements DemoMode, } row.setUserLocked(userLocked); row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); + row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); row.onNotificationUpdated(entry); return true; } @@ -6760,10 +6765,13 @@ public class StatusBar extends SystemUI implements DemoMode, boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(notification, mNotificationData.getImportance(notification.getKey())); entry.row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); + boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded; + entry.row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); boolean applyInPlace; try { applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(), - mNotificationData.isAmbient(key), useIncreasedCollapsedHeight); + mNotificationData.isAmbient(key), useIncreasedCollapsedHeight, + useIncreasedHeadsUp); } catch (RuntimeException e) { Log.e(TAG, "Unable to get notification remote views", e); applyInPlace = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 99e09c6e7e26..67cc5e33d4b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -88,6 +88,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; +import java.util.List; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. @@ -1836,12 +1837,29 @@ public class NotificationStackScrollLayout extends ViewGroup * @return The first child which has visibility unequal to GONE which is currently below the * given translationY or equal to it. */ - private View getFirstChildBelowTranlsationY(float translationY) { + private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) { + if (child.getVisibility() == View.GONE) { + continue; + } + float rowTranslation = child.getTranslationY(); + if (rowTranslation >= translationY) { return child; + } else if (!ignoreChildren && child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { + List<ExpandableNotificationRow> notificationChildren = + row.getNotificationChildren(); + for (int childIndex = 0; childIndex < notificationChildren.size(); + childIndex++) { + ExpandableNotificationRow rowChild = notificationChildren.get(childIndex); + if (rowChild.getTranslationY() + rowTranslation >= translationY) { + return rowChild; + } + } + } } } return null; @@ -2500,7 +2518,7 @@ public class NotificationStackScrollLayout extends ViewGroup View groupParentWhenDismissed = row.getGroupParentWhenDismissed(); nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null ? groupParentWhenDismissed.getTranslationY() - : view.getTranslationY()); + : view.getTranslationY(), true /* ignoreChildren */); } if (nextView != null) { nextView.requestAccessibilityFocus(); @@ -2940,7 +2958,17 @@ public class NotificationStackScrollLayout extends ViewGroup AnimationEvent event = new AnimationEvent(child, animationType); // we need to know the view after this one - event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY()); + float removedTranslation = child.getTranslationY(); + boolean ignoreChildren = true; + if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.isRemoved() && row.wasChildInGroupWhenRemoved()) { + removedTranslation = row.getTranslationWhenRemoved(); + ignoreChildren = false; + } + } + event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation, + ignoreChildren); mAnimationEvents.add(event); mSwipedOutViews.remove(child); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 55085e58c213..98934341b41e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -389,10 +389,25 @@ public class StackStateAnimator { // upwards by default float translationDirection = -1.0f; if (viewState != null) { + float ownPosition = changingView.getTranslationY(); + if (changingView instanceof ExpandableNotificationRow + && event.viewAfterChangingView instanceof ExpandableNotificationRow) { + ExpandableNotificationRow changingRow = + (ExpandableNotificationRow) changingView; + ExpandableNotificationRow nextRow = + (ExpandableNotificationRow) event.viewAfterChangingView; + if (changingRow.isRemoved() + && changingRow.wasChildInGroupWhenRemoved() + && !nextRow.isChildInGroup()) { + // the next row isn't actually a child from a group! Let's + // compare absolute positions! + ownPosition = changingRow.getTranslationWhenRemoved(); + } + } // there was a view after this one, Approximate the distance the next child // travelled translationDirection = ((viewState.yTranslation - - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / + - (ownPosition + actualHeight / 2.0f)) * 2 / actualHeight); translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java index 3058c0a1e739..1df12acde4a5 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java @@ -25,7 +25,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.statusbar.phone.StatusBarIconController; -import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING; +import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING; public class BatteryPreference extends DropDownPreference implements TunerService.Tunable { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 85be4d79d64f..6a92b2f91c5c 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -38,7 +38,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING; +import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index ef743e38d738..ba9e60a9c927 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -20,6 +20,7 @@ package com.android.systemui.util.leak; import android.os.Build; import android.os.Handler; import android.os.Looper; +import android.os.SystemProperties; import android.support.annotation.VisibleForTesting; import com.android.systemui.Dependency; @@ -80,11 +81,15 @@ public class GarbageMonitor { public static class Service extends SystemUI { + // TODO(b/35345376): Turn this back on for debuggable builds after known leak fixed. + private static final boolean ENABLED = Build.IS_DEBUGGABLE + && SystemProperties.getBoolean("debug.enable_leak_reporting", false); + private GarbageMonitor mGarbageMonitor; @Override public void start() { - if (!Build.IS_DEBUGGABLE) { + if (!ENABLED) { return; } mGarbageMonitor = Dependency.get(GarbageMonitor.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java index 09808d47a995..6b47ada8fea7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java @@ -16,6 +16,7 @@ package com.android.systemui; +import com.android.settingslib.graph.BatteryMeterDrawableBase; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyFloat; @@ -41,17 +42,18 @@ import org.junit.runner.RunWith; public class BatteryMeterDrawableTest extends SysuiTestCase { private Resources mResources; - private BatteryMeterDrawable mBatteryMeter; + private BatteryMeterDrawableBase mBatteryMeter; @Before public void setUp() throws Exception { mResources = mContext.getResources(); - mBatteryMeter = new BatteryMeterDrawable(mContext, 0); + mBatteryMeter = new BatteryMeterDrawableBase(mContext, 0); } @Test public void testDrawImageButNoTextIfPluggedIn() { - mBatteryMeter.onBatteryLevelChanged(0, true, true); + mBatteryMeter.setBatteryLevel(0); + mBatteryMeter.setPluggedIn(true); final Canvas canvas = mock(Canvas.class); mBatteryMeter.draw(canvas); verify(canvas, atLeastOnce()).drawPath(any(), any()); @@ -60,7 +62,8 @@ public class BatteryMeterDrawableTest extends SysuiTestCase { @Test public void testDrawTextIfNotPluggedIn() { - mBatteryMeter.onBatteryLevelChanged(0, false, false); + mBatteryMeter.setBatteryLevel(0); + mBatteryMeter.setPluggedIn(false); final Canvas canvas = mock(Canvas.class); mBatteryMeter.draw(canvas); verify(canvas, times(1)).drawText(anyString(), anyFloat(), anyFloat(), any()); @@ -68,8 +71,9 @@ public class BatteryMeterDrawableTest extends SysuiTestCase { @Test public void testDrawNoTextIfPowerSaveEnabled() { - mBatteryMeter.onBatteryLevelChanged(0, false, false); - mBatteryMeter.onPowerSaveChanged(true); + mBatteryMeter.setBatteryLevel(0); + mBatteryMeter.setPluggedIn(false); + mBatteryMeter.setPowerSave(true); final Canvas canvas = mock(Canvas.class); mBatteryMeter.draw(canvas); verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any()); @@ -79,7 +83,8 @@ public class BatteryMeterDrawableTest extends SysuiTestCase { public void testDrawTextWarningAtCriticalLevel() { int criticalLevel = mResources.getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); - mBatteryMeter.onBatteryLevelChanged(criticalLevel, false, false); + mBatteryMeter.setBatteryLevel(criticalLevel); + mBatteryMeter.setPluggedIn(false); final Canvas canvas = mock(Canvas.class); mBatteryMeter.draw(canvas); String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol); @@ -90,7 +95,8 @@ public class BatteryMeterDrawableTest extends SysuiTestCase { public void testDrawTextNoWarningAboveCriticalLevel() { int criticalLevel = mResources.getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); - mBatteryMeter.onBatteryLevelChanged(criticalLevel + 1, false, false); + mBatteryMeter.setBatteryLevel(criticalLevel + 1); + mBatteryMeter.setPluggedIn(false); final Canvas canvas = mock(Canvas.class); mBatteryMeter.draw(canvas); String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java new file mode 100644 index 000000000000..9b868dbff714 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.java @@ -0,0 +1,111 @@ +/* + * 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.systemui.keyguard; + +import static android.app.ActivityManager.TaskDescription; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import android.annotation.ColorInt; +import android.annotation.UserIdInt; +import android.app.KeyguardManager; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Looper; + +import com.android.systemui.keyguard.WorkLockActivity; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * runtest systemui -c com.android.systemui.keyguard.WorkLockActivityTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WorkLockActivityTest { + private static final @UserIdInt int USER_ID = 270; + private static final String TASK_LABEL = "task label"; + + private @Mock DevicePolicyManager mDevicePolicyManager; + private @Mock KeyguardManager mKeyguardManager; + private @Mock Context mContext; + + private WorkLockActivity mActivity; + + private static class WorkLockActivityTestable extends WorkLockActivity { + WorkLockActivityTestable(Context baseContext) { + super(); + attachBaseContext(baseContext); + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(eq(Context.DEVICE_POLICY_SERVICE))) + .thenReturn(mDevicePolicyManager); + when(mContext.getSystemService(eq(Context.KEYGUARD_SERVICE))) + .thenReturn(mKeyguardManager); + + if (Looper.myLooper() == null) { + Looper.prepare(); + } + mActivity = new WorkLockActivityTestable(mContext); + } + + @Test + public void testBackgroundAlwaysOpaque() throws Exception { + final @ColorInt int orgColor = Color.rgb(250, 199, 67); + when(mDevicePolicyManager.getOrganizationColorForUser(eq(USER_ID))).thenReturn(orgColor); + + final @ColorInt int opaqueColor= Color.rgb(164, 198, 57); + final @ColorInt int transparentColor = Color.argb(0, 0, 0, 0); + TaskDescription opaque = new TaskDescription(null, null, opaqueColor); + TaskDescription transparent = new TaskDescription(null, null, transparentColor); + + // When a task description is provided with a suitable (opaque) primaryColor, it should be + // used as the scrim's background color. + mActivity.setIntent(new Intent() + .putExtra(Intent.EXTRA_USER_ID, USER_ID) + .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, opaque)); + assertEquals(opaqueColor, mActivity.getPrimaryColor()); + + // When a task description is provided but has no primaryColor / the primaryColor is + // transparent, the organization color should be used instead. + mActivity.setIntent(new Intent() + .putExtra(Intent.EXTRA_USER_ID, USER_ID) + .putExtra(WorkLockActivity.EXTRA_TASK_DESCRIPTION, transparent)); + assertEquals(orgColor, mActivity.getPrimaryColor()); + + // When no task description is provided at all, it should be treated like a transparent + // description and the organization color shown instead. + mActivity.setIntent(new Intent() + .putExtra(Intent.EXTRA_USER_ID, USER_ID)); + assertEquals(orgColor, mActivity.getPrimaryColor()); + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index 8d43dfbd180d..85bf5c260b7b 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -39,6 +39,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.Rect; @@ -181,6 +182,23 @@ final class AutoFillManagerServiceImpl { updateLocked(); } + CharSequence getServiceName() { + if (mInfo == null) { + return null; + } + final ComponentName serviceComponent = mInfo.getServiceInfo().getComponentName(); + final String packageName = serviceComponent.getPackageName(); + + try { + final PackageManager pm = mContext.getPackageManager(); + final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + return pm.getApplicationLabel(info); + } catch (Exception e) { + Slog.w(TAG, "Could not get label for " + packageName + ": " + e); + return packageName; + } + } + void updateLocked() { ComponentName serviceComponent = null; ServiceInfo serviceInfo = null; @@ -438,14 +456,20 @@ final class AutoFillManagerServiceImpl { final AutoFillId mId; private final Listener mListener; - // // TODO(b/33197203): does it really need a reference to the session's response? - private FillResponse mResponse; + // TODO(b/33197203): would not need a reference to response and session if it was an inner + // class of Session... + private final Session mSession; + // TODO(b/33197203): encapsulate access so it's not called by UI + FillResponse mResponse; + Intent mAuthIntent; + private AutoFillValue mAutoFillValue; private Rect mBounds; private boolean mValueUpdated; - ViewState(AutoFillId id, Listener listener) { + ViewState(Session session, AutoFillId id, Listener listener) { + mSession = session; mId = id; mListener = listener; } @@ -458,6 +482,18 @@ final class AutoFillManagerServiceImpl { maybeCallOnFillReady(); } + /** + * Used when a {@link FillResponse} requires authentication to be unlocked. + */ + void setResponse(FillResponse response, Intent authIntent) { + mAuthIntent = authIntent; + setResponse(response); + } + + CharSequence getServiceName() { + return mSession.getServiceName(); + } + // TODO(b/33197203): need to refactor / rename / document this method to make it clear that // it can change the value and update the UI; similarly, should replace code that // directly sets mAutoFilLValue to use encapsulation. @@ -495,6 +531,7 @@ final class AutoFillManagerServiceImpl { pw.print(prefix); pw.print("value:" ); pw.println(mAutoFillValue); pw.print(prefix); pw.print("updated:" ); pw.println(mValueUpdated); pw.print(prefix); pw.print("bounds:" ); pw.println(mBounds); + pw.print(prefix); pw.print("authIntent:" ); pw.println(mAuthIntent); } } @@ -565,7 +602,6 @@ final class AutoFillManagerServiceImpl { } } - // FillServiceCallbacks @Override public void onFillRequestSuccess(FillResponse response) { @@ -763,7 +799,7 @@ final class AutoFillManagerServiceImpl { ViewState viewState = mViewStates.get(id); if (viewState == null) { - viewState = new ViewState(id, this); + viewState = new ViewState(this, id, this); mViewStates.put(id, viewState); } @@ -844,13 +880,19 @@ final class AutoFillManagerServiceImpl { // TODO(b/33197203): add MetricsLogger calls + if (mCurrentViewState == null) { + // TODO(b/33197203): temporary sanity check; should never happen + Slog.w(TAG, "processResponseLocked(): mCurrentResponse is null"); + return; + } + mCurrentResponse = response; if (mCurrentResponse.getAuthentication() != null) { // Handle authentication. final Intent fillInIntent = createAuthFillInIntent(mStructure); - getUiForShowing().showFillResponseAuthRequest( - mCurrentResponse.getAuthentication(), fillInIntent); + + mCurrentViewState.setResponse(mCurrentResponse, fillInIntent); return; } @@ -864,10 +906,7 @@ final class AutoFillManagerServiceImpl { return; } - // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design - if (mCurrentViewState != null) { - mCurrentViewState.setResponse(mCurrentResponse); - } + mCurrentViewState.setResponse(mCurrentResponse); } void autoFill(Dataset dataset) { @@ -884,6 +923,10 @@ final class AutoFillManagerServiceImpl { } } + CharSequence getServiceName() { + return AutoFillManagerServiceImpl.this.getServiceName(); + } + private Intent createAuthFillInIntent(AssistStructure structure) { Intent fillInIntent = new Intent(); fillInIntent.putExtra(AutoFillManager.EXTRA_ASSIST_STRUCTURE, structure); diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java index 97700405279e..e83dc1e90137 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java @@ -56,19 +56,15 @@ final class AutoFillUI { private static final long SNACK_BAR_LIFETIME_MS = 30 * DateUtils.SECOND_IN_MILLIS; private static final int MSG_HIDE_SNACK_BAR = 1; - private static final String EXTRA_AUTH_INTENT_SENDER = - "com.android.server.autofill.extra.AUTH_INTENT_SENDER"; - private static final String EXTRA_AUTH_FILL_IN_INTENT = - "com.android.server.autofill.extra.AUTH_FILL_IN_INTENT"; - private final Context mContext; private final WindowManager mWm; // TODO(b/33197203) Fix locking - some state requires lock and some not - requires refactoring + private final Object mLock = new Object(); // Fill UI variables private AnchoredWindow mFillWindow; - private DatasetPicker mFillView; + private View mFillView; private ViewState mViewState; private AutoFillUiCallback mCallback; @@ -156,63 +152,76 @@ final class AutoFillUI { UiThread.getHandler().runWithScissors(() -> { hideSnackbarUiThread(); - hideFillResponseAuthUiUiThread(); }, 0); - if (datasets == null) { + if (datasets == null && viewState.mAuthIntent == null) { // TODO(b/33197203): shouldn't be called, but keeping the WTF for a while just to be // safe, otherwise it would crash system server... Slog.wtf(TAG, "showFillUI(): no dataset"); return; } + // TODO(b/33197203): should not display UI after we launched an authentication intent, since + // we have no warranty the provider will call onFailure() if the authentication failed or + // user dismissed the auth window + // because if the service does not handle calling the callback, + + UiThread.getHandler().runWithScissors(() -> { + // The dataset picker is only shown when authentication is not required... + DatasetPicker datasetPicker = null; + if (mViewState == null || !mViewState.mId.equals(viewState.mId)) { hideFillUiUiThread(); mViewState = viewState; - - mFillView = new DatasetPicker(mContext, datasets, - (dataset) -> { - final AutoFillUiCallback callback; - synchronized (mLock) { - callback = mCallback; - } - if (callback != null) { - callback.fill(dataset); - } else { - Slog.w(TAG, "null callback on showFillUi() for " + viewState.mId); - } - hideFillUi(); - }); - + if (viewState.mAuthIntent != null) { + final CharSequence serviceName = viewState.getServiceName(); + + mFillView = new SignInPrompt(mContext, serviceName, (e) -> { + final IntentSender intentSender = viewState.mResponse.getAuthentication(); + final AutoFillUiCallback callback; + final Intent authIntent; + synchronized (mLock) { + callback = mCallback; + authIntent = viewState.mAuthIntent; + // Must reset the authentication intent so UI display the datasets after + // the user authenticated. + viewState.mAuthIntent = null; + } + if (callback != null) { + callback.authenticate(intentSender, authIntent); + } else { + // TODO(b/33197203): need to figure out why it's null sometimes + Slog.w(TAG, "no callback on showFillUi().auth for " + viewState.mId); + } + }); + + } else { + mFillView = datasetPicker = new DatasetPicker(mContext, datasets, + (dataset) -> { + final AutoFillUiCallback callback; + synchronized (mLock) { + callback = mCallback; + } + if (callback != null) { + callback.fill(dataset); + } else { + // TODO(b/33197203): need to figure out why it's null sometimes + Slog.w(TAG, "no callback on showFillUi() for " + viewState.mId); + } + hideFillUiUiThread(); + }); + } mFillWindow = new AnchoredWindow(mWm, appToken, mFillView); - if (DEBUG) Slog.d(TAG, "showFillUi(): view changed"); + if (DEBUG) Slog.d(TAG, "showFillUi(): view changed for: " + viewState.mId); + } + if (datasetPicker != null) { + datasetPicker.update(filterText); } - - if (DEBUG) Slog.d(TAG, "showFillUi(): bounds=" + bounds + ", filterText=" + filterText); - mFillView.update(filterText); mFillWindow.show(bounds); - }, 0); - } - /** - * Shows an UI affordance indicating that user action is required before a {@link - * android.service.autofill.FillResponse} - * can be used. - * - * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to - * autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}. - */ - void showFillResponseAuthRequest(IntentSender intent, Intent fillInIntent) { - if (!hasCallback()) { - return; - } - hideAll(); - UiThread.getHandler().runWithScissors(() -> { - // TODO(b/33197203): proper implementation - showFillResponseAuthUiUiThread(intent, fillInIntent); }, 0); } @@ -250,14 +259,12 @@ final class AutoFillUI { UiThread.getHandler().runWithScissors(() -> { hideSnackbarUiThread(); hideFillUiUiThread(); - hideFillResponseAuthUiUiThread(); }, 0); } void dump(PrintWriter pw) { pw.println("AufoFill UI"); final String prefix = " "; - pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar); pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState); @@ -310,103 +317,4 @@ final class AutoFillUI { void fill(Dataset dataset); void save(); } - - ///////////////////////////////////////////////////////////////////////////////// - // TODO(b/33197203): temporary code using a notification to request auto-fill. // - // Will be removed once UX decide the right way to present it to the user. // - ///////////////////////////////////////////////////////////////////////////////// - - // TODO(b/33197203): remove from frameworks/base/core/res/AndroidManifest.xml once not used - private static final String NOTIFICATION_AUTO_FILL_INTENT = - "com.android.internal.autofill.action.REQUEST_AUTOFILL"; - - private BroadcastReceiver mNotificationReceiver; - private final Object mLock = new Object(); - - // Hack used to generate unique pending intents - static int sResultCode = 0; - - private void ensureNotificationListener() { - synchronized (mLock) { - if (mNotificationReceiver == null) { - mNotificationReceiver = new NotificationReceiver(); - mContext.registerReceiver(mNotificationReceiver, - new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT)); - } - } - } - - final class NotificationReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final AutoFillUiCallback callback; - synchronized (mLock) { - callback = mCallback; - } - if (callback != null) { - IntentSender intentSender = intent.getParcelableExtra(EXTRA_AUTH_INTENT_SENDER); - Intent fillInIntent = intent.getParcelableExtra(EXTRA_AUTH_FILL_IN_INTENT); - callback.authenticate(intentSender, fillInIntent); - } - collapseStatusBar(); - } - } - - @android.annotation.UiThread - private void showFillResponseAuthUiUiThread(IntentSender intent, Intent fillInIntent) { - final String title = "AutoFill Authentication"; - final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n"); - - final Intent authIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT); - authIntent.putExtra(EXTRA_AUTH_INTENT_SENDER, intent); - authIntent.putExtra(EXTRA_AUTH_FILL_IN_INTENT, fillInIntent); - - final PendingIntent authPendingIntent = PendingIntent.getBroadcast( - mContext, ++sResultCode, authIntent, PendingIntent.FLAG_ONE_SHOT); - - subTitle.append("Tap notification to launch its authentication UI."); - - final Notification.Builder notification = newNotificationBuilder() - .setAutoCancel(true) - .setOngoing(false) - .setContentTitle(title) - .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString())) - .setContentIntent(authPendingIntent); - - ensureNotificationListener(); - - final long identity = Binder.clearCallingIdentity(); - try { - NotificationManager.from(mContext).notify(0, notification.build()); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @android.annotation.UiThread - private void hideFillResponseAuthUiUiThread() { - final long identity = Binder.clearCallingIdentity(); - try { - NotificationManager.from(mContext).cancel(0); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private Notification.Builder newNotificationBuilder() { - return new Notification.Builder(mContext) - .setCategory(Notification.CATEGORY_SYSTEM) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setLocalOnly(true) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); - } - - private void collapseStatusBar() { - final StatusBarManager sbm = (StatusBarManager) mContext.getSystemService("statusbar"); - sbm.collapsePanels(); - } - ///////////////////////////////////////// - // End of temporary notification code. // - ///////////////////////////////////////// } diff --git a/services/autofill/java/com/android/server/autofill/SignInPrompt.java b/services/autofill/java/com/android/server/autofill/SignInPrompt.java new file mode 100644 index 000000000000..6d17acda4bda --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/SignInPrompt.java @@ -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. + */ +package com.android.server.autofill; + +import android.content.Context; +import android.view.View; +import android.widget.Button; + +/** + * A view displaying the sign-in prompt for an auto-fill service. + */ +final class SignInPrompt extends Button { + + SignInPrompt(Context context, CharSequence serviceName, View.OnClickListener listener) { + super(context); + // TODO(b/33197203): use strings.xml + final String text = serviceName != null + ? "Sign in to " + serviceName + " to autofill" + : "Sign in to autofill"; + + // TODO(b/33197203): polish UI / use better altenative than a button... + setText(text); + setOnClickListener(listener); + } +} diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index b6b3c43a597b..dc987fac97e5 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -149,11 +149,15 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.nio.charset.StandardCharsets; import java.security.InvalidParameterException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; /** * This class provides a system service that manages input methods. @@ -525,20 +529,189 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * </p> */ private static class StartInputInfo { + private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); + + final int mSequenceNumber; + final long mTimestamp; + final long mWallTime; @NonNull final IBinder mImeToken; + @NonNull + final String mImeId; + // @InputMethodClient.StartInputReason + final int mStartInputReason; + final boolean mRestarting; @Nullable final IBinder mTargetWindow; - - StartInputInfo(@NonNull IBinder imeToken, @Nullable IBinder targetWindow) { + @NonNull + final EditorInfo mEditorInfo; + final int mTargetWindowSoftInputMode; + final int mClientBindSequenceNumber; + + StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId, + /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting, + @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo, + int targetWindowSoftInputMode, int clientBindSequenceNumber) { + mSequenceNumber = sSequenceNumber.getAndIncrement(); + mTimestamp = SystemClock.uptimeMillis(); + mWallTime = System.currentTimeMillis(); mImeToken = imeToken; + mImeId = imeId; + mStartInputReason = startInputReason; + mRestarting = restarting; mTargetWindow = targetWindow; + mEditorInfo = editorInfo; + mTargetWindowSoftInputMode = targetWindowSoftInputMode; + mClientBindSequenceNumber = clientBindSequenceNumber; } } @GuardedBy("mMethodMap") private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>(); + /** + * A ring buffer to store the history of {@link StartInputInfo}. + */ + private static final class StartInputHistory { + /** + * Entry size for non low-RAM devices. + * + * <p>TODO: Consider to follow what other system services have been doing to manage + * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p> + */ + private final static int ENTRY_SIZE_FOR_HIGH_RAM_DEVICE = 16; + + /** + * Entry size for non low-RAM devices. + * + * <p>TODO: Consider to follow what other system services have been doing to manage + * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p> + */ + private final static int ENTRY_SIZE_FOR_LOW_RAM_DEVICE = 5; + + private static int getEntrySize() { + if (ActivityManager.isLowRamDeviceStatic()) { + return ENTRY_SIZE_FOR_LOW_RAM_DEVICE; + } else { + return ENTRY_SIZE_FOR_HIGH_RAM_DEVICE; + } + } + + /** + * Backing store for the ring bugger. + */ + private final Entry[] mEntries = new Entry[getEntrySize()]; + + /** + * An index of {@link #mEntries}, to which next {@link #addEntry(StartInputInfo)} should + * write. + */ + private int mNextIndex = 0; + + /** + * Recyclable entry to store the information in {@link StartInputInfo}. + */ + private static final class Entry { + int mSequenceNumber; + long mTimestamp; + long mWallTime; + @NonNull + String mImeTokenString; + @NonNull + String mImeId; + /* @InputMethodClient.StartInputReason */ + int mStartInputReason; + boolean mRestarting; + @NonNull + String mTargetWindowString; + @NonNull + EditorInfo mEditorInfo; + int mTargetWindowSoftInputMode; + int mClientBindSequenceNumber; + + Entry(@NonNull StartInputInfo original) { + set(original); + } + + void set(@NonNull StartInputInfo original) { + mSequenceNumber = original.mSequenceNumber; + mTimestamp = original.mTimestamp; + mWallTime = original.mWallTime; + // Intentionally convert to String so as not to keep a strong reference to a Binder + // object. + mImeTokenString = String.valueOf(original.mImeToken); + mImeId = original.mImeId; + mStartInputReason = original.mStartInputReason; + mRestarting = original.mRestarting; + // Intentionally convert to String so as not to keep a strong reference to a Binder + // object. + mTargetWindowString = String.valueOf(original.mTargetWindow); + mEditorInfo = original.mEditorInfo; + mTargetWindowSoftInputMode = original.mTargetWindowSoftInputMode; + mClientBindSequenceNumber = original.mClientBindSequenceNumber; + } + } + + /** + * Add a new entry and discard the oldest entry as needed. + * @param info {@lin StartInputInfo} to be added. + */ + void addEntry(@NonNull StartInputInfo info) { + final int index = mNextIndex; + if (mEntries[index] == null) { + mEntries[index] = new Entry(info); + } else { + mEntries[index].set(info); + } + mNextIndex = (mNextIndex + 1) % mEntries.length; + } + + void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + final SimpleDateFormat dataFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + + for (int i = 0; i < mEntries.length; ++i) { + final Entry entry = mEntries[(i + mNextIndex) % mEntries.length]; + if (entry == null) { + continue; + } + pw.print(prefix); + pw.println("StartInput #" + entry.mSequenceNumber + ":"); + + pw.print(prefix); + pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime)) + + " (timestamp=" + entry.mTimestamp + ")" + + " reason=" + + InputMethodClient.getStartInputReason(entry.mStartInputReason) + + " restarting=" + entry.mRestarting); + + pw.print(prefix); + pw.println(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]"); + + pw.print(prefix); + pw.println(" targetWin=" + entry.mTargetWindowString + + " [" + entry.mEditorInfo.packageName + "]" + + " clientBindSeq=" + entry.mClientBindSequenceNumber); + + pw.print(prefix); + pw.println(" softInputMode=" + InputMethodClient.softInputModeToString( + entry.mTargetWindowSoftInputMode)); + + pw.print(prefix); + pw.println(" inputType=0x" + Integer.toHexString(entry.mEditorInfo.inputType) + + " imeOptions=0x" + Integer.toHexString(entry.mEditorInfo.imeOptions) + + " fieldId=0x" + Integer.toHexString(entry.mEditorInfo.fieldId) + + " fieldName=" + entry.mEditorInfo.fieldName + + " actionId=" + entry.mEditorInfo.actionId + + " actionLabel=" + entry.mEditorInfo.actionLabel); + } + } + } + + @GuardedBy("mMethodMap") + @NonNull + private final StartInputHistory mStartInputHistory = new StartInputHistory(); + class SettingsObserver extends ContentObserver { int mUserId; boolean mRegistered = false; @@ -1380,8 +1553,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final Binder startInputToken = new Binder(); - final StartInputInfo info = new StartInputInfo(mCurToken, mCurFocusedWindow); + final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason, + !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode, + mCurSeq); mStartInputMap.put(startInputToken, info); + mStartInputHistory.addEntry(info); final SessionState session = mCurClient.curSession; executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO( @@ -4156,6 +4332,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingController.dump(p); p.println(" mSettings:"); mSettings.dumpLocked(p, " "); + + p.println(" mStartInputHistory:"); + mStartInputHistory.dump(pw, " "); } p.println(" "); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 072b0ea03712..a73eb18b3c61 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9623,6 +9623,20 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public ActivityManager.TaskDescription getTaskDescription(int id) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, + "getTaskDescription()"); + final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked( + id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID); + if (tr != null) { + return tr.lastTaskDescription; + } + } + return null; + } + + @Override public int addAppTask(IBinder activityToken, Intent intent, ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException { final int callingUid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index bf1018f6a079..a95a627d54e6 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -103,6 +103,10 @@ public final class PlaybackActivityMonitor final boolean change; synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); + // FIXME SoundPool not ready for state reporting + if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { + return; + } if (checkConfigurationCaller(piid, apc, binderUid)) { //TODO add generation counter to only update to the latest state change = apc.handleStateEvent(event); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 9d63462d6abc..6c608a28089d 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1008,10 +1008,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return false; } - protected boolean requestUpstreamMobileConnection() { + protected void requestUpstreamMobileConnection() { mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); mUpstreamNetworkMonitor.registerMobileNetworkRequest(); - return true; } protected void unrequestUpstreamMobileConnection() { @@ -1058,9 +1057,13 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } protected void chooseUpstreamType(boolean tryCell) { + final int upstreamType = findPreferredUpstreamType(tryCell); + setUpstreamByType(upstreamType); + } + + protected int findPreferredUpstreamType(boolean tryCell) { final ConnectivityManager cm = getConnectivityManager(); int upType = ConnectivityManager.TYPE_NONE; - String iface = null; updateConfiguration(); // TODO - remove? @@ -1100,7 +1103,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering requestUpstreamMobileConnection(); break; case ConnectivityManager.TYPE_NONE: - if (tryCell && requestUpstreamMobileConnection()) { + if (tryCell) { + requestUpstreamMobileConnection(); // We think mobile should be coming up; don't set a retry. } else { sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); @@ -1117,7 +1121,13 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering break; } + return upType; + } + + protected void setUpstreamByType(int upType) { + final ConnectivityManager cm = getConnectivityManager(); Network network = null; + String iface = null; if (upType != ConnectivityManager.TYPE_NONE) { LinkProperties linkProperties = cm.getLinkProperties(upType); if (linkProperties != null) { @@ -1354,9 +1364,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering simChange.startListening(); mUpstreamNetworkMonitor.start(); - mTryCell = true; // better try something first pass or crazy tests cases will fail - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; + // Better try something first pass or crazy tests cases will fail. + chooseUpstreamType(true); + mTryCell = false; } @Override @@ -1407,10 +1417,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering break; } case CMD_UPSTREAM_CHANGED: - // need to try DUN immediately if Wifi goes down - mTryCell = true; - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; + // Need to try DUN immediately if Wi-Fi goes down. + chooseUpstreamType(true); + mTryCell = false; break; case CMD_RETRY_UPSTREAM: chooseUpstreamType(mTryCell); diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 37221a971ad1..5e5157913f20 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -250,31 +250,33 @@ public class TetherInterfaceStateMachine extends StateMachine { } private void cleanupUpstream() { - if (mMyUpstreamIfaceName != null) { - // note that we don't care about errors here. - // sometimes interfaces are gone before we get - // to remove their rules, which generates errors. - // just do the best we can. - try { - // about to tear down NAT; gather remaining statistics - mStatsService.forceUpdate(); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); - } - try { - mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName); - } catch (Exception e) { - if (VDBG) Log.e( - TAG, "Exception in removeInterfaceForward: " + e.toString()); - } - try { - mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); - } - mMyUpstreamIfaceName = null; + if (mMyUpstreamIfaceName == null) return; + + cleanupUpstreamInterface(mMyUpstreamIfaceName); + mMyUpstreamIfaceName = null; + } + + private void cleanupUpstreamInterface(String upstreamIface) { + // Note that we don't care about errors here. + // Sometimes interfaces are gone before we get + // to remove their rules, which generates errors. + // Just do the best we can. + try { + // About to tear down NAT; gather remaining statistics. + mStatsService.forceUpdate(); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); + } + try { + mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString()); + } + try { + mNMService.disableNat(mIfaceName, upstreamIface); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); } - return; } @Override @@ -306,6 +308,7 @@ public class TetherInterfaceStateMachine extends StateMachine { newUpstreamIfaceName); } catch (Exception e) { Log.e(TAG, "Exception enabling Nat: " + e.toString()); + cleanupUpstreamInterface(newUpstreamIfaceName); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 778ace200c80..68d993d41c97 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2458,12 +2458,14 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); return null; } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - writePolicyXml(baos, true /*forBackup*/); - return baos.toByteArray(); - } catch (IOException e) { - Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + synchronized(mPolicyFile) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writePolicyXml(baos, true /*forBackup*/); + return baos.toByteArray(); + } catch (IOException e) { + Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + } } return null; } @@ -2481,12 +2483,14 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); return; } - final ByteArrayInputStream bais = new ByteArrayInputStream(payload); - try { - readPolicyXml(bais, true /*forRestore*/); - savePolicyFile(); - } catch (NumberFormatException | XmlPullParserException | IOException e) { - Slog.w(TAG, "applyRestore: error reading payload", e); + synchronized(mPolicyFile) { + final ByteArrayInputStream bais = new ByteArrayInputStream(payload); + try { + readPolicyXml(bais, true /*forRestore*/); + savePolicyFile(); + } catch (NumberFormatException | XmlPullParserException | IOException e) { + Slog.w(TAG, "applyRestore: error reading payload", e); + } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ebd0b343f9cf..0d63f72ea37f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -126,6 +126,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.AppsQueryHelper; +import android.content.pm.ChangedPackages; import android.content.pm.ComponentInfo; import android.content.pm.InstantAppInfo; import android.content.pm.EphemeralRequest; @@ -720,6 +721,21 @@ public class PackageManagerService extends IPackageManager.Stub { private final InstantAppRegistry mInstantAppRegistry; + @GuardedBy("mPackages") + int mChangedPackagesSequenceNumber; + /** + * List of changed [installed, removed or updated] packages. + * mapping from user id -> sequence number -> package name + */ + @GuardedBy("mPackages") + final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>(); + /** + * The sequence number of the last change to a package. + * mapping from user id -> package name -> sequence number + */ + @GuardedBy("mPackages") + final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>(); + public static final class SharedLibraryEntry { public final String path; public final String apk; @@ -2141,6 +2157,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM); } } + scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM); } } @@ -4220,6 +4237,52 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private void updateSequenceNumberLP(String packageName, int[] userList) { + for (int i = userList.length - 1; i >= 0; --i) { + final int userId = userList[i]; + SparseArray<String> changedPackages = mChangedPackages.get(userId); + if (changedPackages == null) { + changedPackages = new SparseArray<>(); + mChangedPackages.put(userId, changedPackages); + } + Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId); + if (sequenceNumbers == null) { + sequenceNumbers = new HashMap<>(); + mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers); + } + final Integer sequenceNumber = sequenceNumbers.get(packageName); + if (sequenceNumber != null) { + changedPackages.remove(sequenceNumber); + } + changedPackages.put(mChangedPackagesSequenceNumber, packageName); + sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber); + } + mChangedPackagesSequenceNumber++; + } + + @Override + public ChangedPackages getChangedPackages(int sequenceNumber, int userId) { + synchronized (mPackages) { + if (sequenceNumber >= mChangedPackagesSequenceNumber) { + return null; + } + final SparseArray<String> changedPackages = mChangedPackages.get(userId); + if (changedPackages == null) { + return null; + } + final List<String> packageNames = + new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber); + for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) { + final String packageName = changedPackages.get(i); + if (packageName != null) { + packageNames.add(packageName); + } + } + return packageNames.isEmpty() + ? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames); + } + } + @Override public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() { ArrayList<FeatureInfo> res; @@ -13251,6 +13314,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkgSetting.setHidden(false, userId); pkgSetting.setInstallReason(installReason, userId); mSettings.writePackageRestrictionsLPr(userId); + mSettings.writeKernelMappingLPr(pkgSetting); installed = true; } } @@ -13263,6 +13327,9 @@ public class PackageManagerService extends IPackageManager.Stub { } } sendPackageAddedForUser(packageName, pkgSetting, userId); + synchronized (mPackages) { + updateSequenceNumberLP(packageName, new int[]{ userId }); + } } } finally { Binder.restoreCallingIdentity(callingId); @@ -16345,6 +16412,7 @@ public class PackageManagerService extends IPackageManager.Stub { //note that the new package setting would have already been //added to mPackages. It hasn't been persisted yet. mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE); + // TODO: Remove this write? It's also written at the end of this method Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); mSettings.writeLPr(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -16418,6 +16486,7 @@ public class PackageManagerService extends IPackageManager.Stub { } else if (!previousUserIds.contains(userId)) { ps.setInstallReason(installReason, userId); } + mSettings.writeKernelMappingLPr(ps); } res.name = pkgName; res.uid = newPackage.applicationInfo.uid; @@ -16847,6 +16916,10 @@ public class PackageManagerService extends IPackageManager.Stub { sUserManager.getUserIds(), true); } } + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + updateSequenceNumberLP(pkgName, res.newUsers); + } } } @@ -17429,6 +17502,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (res) { mInstantAppRegistry.onPackageUninstalledLPw(uninstalledPs.pkg, info.removedUsers); + updateSequenceNumberLP(packageName, info.removedUsers); } } } @@ -17597,6 +17671,7 @@ public class PackageManagerService extends IPackageManager.Stub { // writer synchronized (mPackages) { + boolean installedStateChanged = false; if (deletedPs != null) { if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); @@ -17644,6 +17719,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_REMOVE) { Slog.d(TAG, " user " + userId + " => " + installed); } + if (installed != ps.getInstalled(userId)) { + installedStateChanged = true; + } ps.setInstalled(installed, userId); } } @@ -17653,6 +17731,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Save settings now mSettings.writeLPr(); } + if (installedStateChanged) { + mSettings.writeKernelMappingLPr(ps); + } } if (removedAppId != -1) { // A user ID was deleted here. Go through all users and remove it @@ -17795,6 +17876,7 @@ public class PackageManagerService extends IPackageManager.Stub { UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG); if (applyUserRestrictions) { + boolean installedStateChanged = false; if (DEBUG_REMOVE) { Slog.d(TAG, "Propagating install state across reinstall"); } @@ -17803,6 +17885,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_REMOVE) { Slog.d(TAG, " user " + userId + " => " + installed); } + if (installed != ps.getInstalled(userId)) { + installedStateChanged = true; + } ps.setInstalled(installed, userId); mSettings.writeRuntimePermissionsForUserLPr(userId, false); @@ -17810,6 +17895,9 @@ public class PackageManagerService extends IPackageManager.Stub { // Regardless of writeSettings we need to ensure that this restriction // state propagation is persisted mSettings.writeAllUsersPackageRestrictionsLPr(); + if (installedStateChanged) { + mSettings.writeKernelMappingLPr(ps); + } } // can downgrade to reader here if (writeSettings) { @@ -18013,6 +18101,7 @@ public class PackageManagerService extends IPackageManager.Stub { // broadcasts will be sent correctly. if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete"); ps.setInstalled(true, user.getIdentifier()); + mSettings.writeKernelMappingLPr(ps); } } else { // This is a system app, so we assume that the @@ -18122,6 +18211,7 @@ public class PackageManagerService extends IPackageManager.Stub { ps.readUserState(nextUserId).domainVerificationStatus, 0, PackageManager.INSTALL_REASON_UNKNOWN); } + mSettings.writeKernelMappingLPr(ps); } private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId, @@ -19725,6 +19815,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } scheduleWritePackageRestrictionsLocked(userId); + updateSequenceNumberLP(packageName, new int[] { userId }); components = mPendingBroadcasts.get(userId, packageName); final boolean newPackage = components == null; if (newPackage) { diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index b63edfdd0d73..0e11b0cec4c2 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -40,6 +40,9 @@ import java.util.Set; * Settings base class for pending and resolved classes. */ abstract class PackageSettingBase extends SettingBase { + + private static final int[] EMPTY_INT_ARRAY = new int[0]; + /** * Indicates the state of installation. Used by PackageManager to figure out * incomplete installations. Say a package is being installed (the state is @@ -502,6 +505,25 @@ abstract class PackageSettingBase extends SettingBase { userState.delete(userId); } + public int[] getNotInstalledUserIds() { + int count = 0; + int userStateCount = userState.size(); + for (int i = 0; i < userStateCount; i++) { + if (userState.valueAt(i).installed == false) { + count++; + } + } + if (count == 0) return EMPTY_INT_ARRAY; + int[] excludedUserIds = new int[count]; + int idx = 0; + for (int i = 0; i < userStateCount; i++) { + if (userState.valueAt(i).installed == false) { + excludedUserIds[idx++] = userState.keyAt(i); + } + } + return excludedUserIds; + } + IntentFilterVerificationInfo getIntentFilterVerificationInfo() { return verificationInfo; } diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index 27811508d573..7e7de21eb982 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -72,6 +72,9 @@ public final class SELinuxMMAC { // Append ephemeral to existing seinfo label private static final String EPHEMERAL_APP_STR = ":ephemeralapp"; + // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion + private static final String TARGETSDKVERSION_STR = ":targetSdkVersion="; + /** * Load the mac_permissions.xml file containing all seinfo assignments used to * label apps. The loaded mac_permissions.xml file is determined by the @@ -296,6 +299,8 @@ public final class SELinuxMMAC { if (pkg.applicationInfo.isPrivilegedApp()) pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR; + pkg.applicationInfo.seinfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion; + if (DEBUG_POLICY_INSTALL) { Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " + "seinfo=" + pkg.applicationInfo.seinfo); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 281e445e59b4..61568022eff2 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -252,6 +252,7 @@ final class Settings { private final File mPackageListFilename; private final File mStoppedPackagesFilename; private final File mBackupStoppedPackagesFilename; + /** The top level directory in configfs for sdcardfs to push the package->uid,userId mappings */ private final File mKernelMappingFilename; /** Map from package name to settings */ @@ -260,8 +261,8 @@ final class Settings { /** List of packages that installed other packages */ final ArraySet<String> mInstallerPackages = new ArraySet<>(); - /** Map from package name to appId */ - private final ArrayMap<String, Integer> mKernelMapping = new ArrayMap<>(); + /** Map from package name to appId and excluded userids */ + private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>(); // List of replaced system applications private final ArrayMap<String, PackageSetting> mDisabledSysPackages = @@ -271,6 +272,11 @@ final class Settings { private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications = new ArrayMap<String, IntentFilterVerificationInfo>(); + private static final class KernelPackageState { + int appId; + int[] excludedUserIds; + } + // Bookkeeping for restored user permission grants final class RestoredPermissionGrant { String permissionName; @@ -2512,6 +2518,15 @@ final class Settings { //Debug.stopMethodTracing(); } + private void writeKernelRemoveUserLPr(int userId) { + if (mKernelMappingFilename == null) return; + + File removeUserIdFile = new File(mKernelMappingFilename, "remove_userid"); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + userId + " to " + removeUserIdFile + .getAbsolutePath()); + writeIntToFile(removeUserIdFile, userId); + } + void writeKernelMappingLPr() { if (mKernelMappingFilename == null) return; @@ -2538,27 +2553,63 @@ final class Settings { } void writeKernelMappingLPr(PackageSetting ps) { - if (mKernelMappingFilename == null) return; + if (mKernelMappingFilename == null || ps == null || ps.name == null) return; - final Integer cur = mKernelMapping.get(ps.name); - if (cur != null && cur.intValue() == ps.appId) { - // Ignore when mapping already matches - return; + KernelPackageState cur = mKernelMapping.get(ps.name); + final boolean firstTime = cur == null; + int[] excludedUserIds = ps.getNotInstalledUserIds(); + final boolean userIdsChanged = firstTime + || !Arrays.equals(excludedUserIds, cur.excludedUserIds); + + // Package directory + final File dir = new File(mKernelMappingFilename, ps.name); + + if (firstTime) { + dir.mkdir(); + // Create a new mapping state + cur = new KernelPackageState(); + mKernelMapping.put(ps.name, cur); } - if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId); + // If mapping is incorrect or non-existent, write the appid file + if (cur.appId != ps.appId) { + final File appIdFile = new File(dir, "appid"); + writeIntToFile(appIdFile, ps.appId); + if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId); + } - final File dir = new File(mKernelMappingFilename, ps.name); - dir.mkdir(); + if (userIdsChanged) { + // Build the exclusion list -- the ids to add to the exclusion list + for (int i = 0; i < excludedUserIds.length; i++) { + if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds, + excludedUserIds[i])) { + writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to " + + ps.name + "/excluded_userids"); + } + } + // Build the inclusion list -- the ids to remove from the exclusion list + if (cur.excludedUserIds != null) { + for (int i = 0; i < cur.excludedUserIds.length; i++) { + if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) { + writeIntToFile(new File(dir, "clear_userid"), + cur.excludedUserIds[i]); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to " + + ps.name + "/clear_userid"); - final File file = new File(dir, "appid"); + } + } + } + cur.excludedUserIds = excludedUserIds; + } + } + + private void writeIntToFile(File file, int value) { try { - // Note that the use of US_ASCII here is safe, we're only writing a decimal - // number to the file. FileUtils.bytesToFile(file.getAbsolutePath(), - Integer.toString(ps.appId).getBytes(StandardCharsets.US_ASCII)); - mKernelMapping.put(ps.name, ps.appId); + Integer.toString(value).getBytes(StandardCharsets.US_ASCII)); } catch (IOException ignored) { + Slog.w(TAG, "Couldn't write " + value + " to " + file.getAbsolutePath()); } } @@ -4081,6 +4132,9 @@ final class Settings { !ArrayUtils.contains(disallowedPackages, ps.name); // Only system apps are initially installed. ps.setInstalled(shouldInstall, userHandle); + if (!shouldInstall) { + writeKernelMappingLPr(ps); + } // Need to create a data directory for all apps under this user. Accumulate all // required args and call the installer after mPackages lock has been released volumeUuids[i] = ps.volumeUuid; @@ -4123,6 +4177,10 @@ final class Settings { mRuntimePermissionsPersistence.onUserRemovedLPw(userId); writePackageListLPr(); + + // Inform kernel that the user was removed, so that packages are marked uninstalled + // for sdcardfs + writeKernelRemoveUserLPr(userId); } void removeCrossProfileIntentFiltersLPw(int userId) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 544d1e3d88db..b09d69996974 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -423,8 +423,12 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye mBoundsAfterRotation.setEmpty(); final DockedStackDividerController controller = getDisplayContent() .mDividerControllerLocked; - if (controller.isMinimizedDock() && mStackId == DOCKED_STACK_ID) { - outTempBounds.set(controller.getMiddlePositionDockedStackRect()); + if (mStackId == DOCKED_STACK_ID) { + final Rect dockedStackRect = controller.getMiddlePositionDockedStackRect(); + + if (dockedStackRect != null) { + outTempBounds.set(dockedStackRect); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c68a1ae4f9d0..2b2bb48c91a3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7211,6 +7211,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = mInjector.binderClearCallingIdentity(); try { mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle); + mIPackageManager.flushPackageRestrictionsAsUser(userHandle); } catch (RemoteException re) { // Shouldn't happen } finally { @@ -7229,6 +7230,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = mInjector.binderClearCallingIdentity(); try { mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle); + mIPackageManager.flushPackageRestrictionsAsUser(userHandle); } catch (RemoteException re) { // Shouldn't happen } finally { @@ -10042,7 +10044,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isDeviceProvisioned() { - return !TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT)); + synchronized (this) { + return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete; + } } private void removePackageIfRequired(final String packageName, final int userId) { diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 0dcd0f14dd38..d51d75ee9f95 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; @@ -374,6 +375,12 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public ChangedPackages getChangedPackages(int sequenceNumber) { + throw new UnsupportedOperationException(); + } + @Override public ResolveInfo resolveActivity(Intent intent, int flags) { throw new UnsupportedOperationException(); diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java index 0fa45454fa70..32e1b96cf798 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java @@ -210,10 +210,9 @@ public class TetherInterfaceStateMachineTest { inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); - // TODO: Verify proper cleanup is performed: - // inOrder.verify(mStatsService).forceUpdate(); - // inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - // inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); } @Test @@ -230,10 +229,9 @@ public class TetherInterfaceStateMachineTest { inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - // TODO: Verify proper cleanup is performed: - // inOrder.verify(mStatsService).forceUpdate(); - // inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - // inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); } @Test @@ -296,6 +294,18 @@ public class TetherInterfaceStateMachineTest { IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); } + @Test + public void ignoresDuplicateUpstreamNotifications() throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + + verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + + for (int i = 0; i < 5; i++) { + dispatchTetherConnectionChanged(UPSTREAM_IFACE); + verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + } + } + /** * Send a command to the state machine under test, and run the event loop to idle. * diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index 35cf9038f9ae..9bc8e18a0fa8 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -31,6 +31,7 @@ import com.android.resources.ResourceType; import android.annotation.Nullable; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -697,6 +698,22 @@ public final class BridgeTypedArray extends TypedArray { /** + * Retrieve the Typeface for the attribute at <var>index</var>. + * @param index Index of attribute to retrieve. + * + * @return Typeface for the attribute, or null if not defined. + */ + @Override + public Typeface getFont(int index) { + if (!hasValue(index)) { + return null; + } + + ResourceValue value = mResourceData[index]; + return ResourceHelper.getFont(value, mContext, mTheme); + } + + /** * Retrieve the CharSequence[] for the attribute at <var>index</var>. * This gets the resource ID of the selected attribute, and uses * {@link Resources#getTextArray Resources.getTextArray} of the owning diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java index e0f8e1c33bc6..d71cc6f7ddf0 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java @@ -43,6 +43,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.icu.text.PluralRules; import android.util.AttributeSet; @@ -779,6 +780,35 @@ public class Resources_Delegate { } @LayoutlibDelegate + static Typeface getFont(Resources resources, int id) throws + NotFoundException { + Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag); + if (value != null) { + return ResourceHelper.getFont(value.getSecond(), resources.mContext, null); + } + + throwException(resources, id); + + // this is not used since the method above always throws + return null; + } + + @LayoutlibDelegate + static Typeface getFont(Resources resources, TypedValue outValue, int id) throws + NotFoundException { + Resources_Delegate.getValue(resources, id, outValue, true); + if (outValue.string != null) { + return ResourceHelper.getFont(outValue.string.toString(), resources.mContext, null, + mPlatformResourceFlag[0]); + } + + throwException(resources, id); + + // this is not used since the method above always throws + return null; + } + + @LayoutlibDelegate static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException { Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag); diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index a43e54579da2..fb24c01086d7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -344,7 +344,9 @@ public class FontFamily_Delegate { ffd.addFont(fontInfo); return true; } - fontStream = assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING); + fontStream = isAsset ? + assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING) : + assetRepository.openNonAsset(cookie, path, AssetManager.ACCESS_STREAMING); if (fontStream == null) { Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path, path); diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java new file mode 100644 index 000000000000..ce669cba9c47 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.annotation.NonNull; + +/** + * Class allowing access to package-protected methods/fields. + */ +public class Typeface_Accessor { + public static boolean isSystemFont(@NonNull String fontName) { + return Typeface.sSystemFontMap.containsKey(fontName); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index 00799a1c6bb6..00394763f1b3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ChangedPackages; import android.content.pm.InstantAppInfo; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; @@ -859,6 +860,11 @@ public class BridgePackageManager extends PackageManager { } @Override + public ChangedPackages getChangedPackages(int sequenceNumber) { + return null; + } + + @Override public boolean isUpgrade() { return false; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index c197e40eb4cf..b3a2d3e27f88 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -38,16 +38,20 @@ import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.ComplexColor; import android.content.res.ComplexColor_Accessor; +import android.content.res.FontResourcesParser; import android.content.res.GradientColor; import android.content.res.Resources.Theme; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.NinePatch_Delegate; import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.Typeface_Accessor; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; +import android.text.FontConfig; import android.util.TypedValue; import java.io.File; @@ -367,6 +371,89 @@ public final class ResourceHelper { return null; } + /** + * Returns a {@link Typeface} given a font name. The font name, can be a system font family + * (like sans-serif) or a full path if the font is to be loaded from resources. + */ + public static Typeface getFont(String fontName, BridgeContext context, Theme theme, boolean + isFramework) { + if (fontName == null) { + return null; + } + + if (Typeface_Accessor.isSystemFont(fontName)) { + // Shortcut for the case where we are asking for a system font name. Those are not + // loaded using external resources. + return null; + } + + // Check if this is an asset that we've already loaded dynamically + Typeface typeface = Typeface.findFromCache(context.getAssets(), fontName); + if (typeface != null) { + return typeface; + } + + String lowerCaseValue = fontName.toLowerCase(); + if (lowerCaseValue.endsWith(".xml")) { + // create a block parser for the file + Boolean psiParserSupport = context.getLayoutlibCallback().getFlag( + RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT); + XmlPullParser parser = null; + if (psiParserSupport != null && psiParserSupport) { + parser = context.getLayoutlibCallback().getXmlFileParser(fontName); + } + else { + File f = new File(fontName); + if (f.isFile()) { + try { + parser = ParserFactory.create(f); + } catch (XmlPullParserException | FileNotFoundException e) { + // this is an error and not warning since the file existence is checked before + // attempting to parse it. + Bridge.getLog().error(null, "Failed to parse file " + fontName, + e, null /*data*/); + } + } + } + + if (parser != null) { + BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( + parser, context, isFramework); + try { + FontConfig config = FontResourcesParser.parse(blockParser, context + .getResources()); + typeface = Typeface.createFromResources(config, context.getAssets(), + fontName); + } catch (XmlPullParserException | IOException e) { + Bridge.getLog().error(null, "Failed to parse file " + fontName, + e, null /*data*/); + } finally { + blockParser.ensurePopped(); + } + } else { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + String.format("File %s does not exist (or is not a file)", fontName), + null /*data*/); + } + } else { + typeface = Typeface.createFromResources(context.getAssets(), fontName, 0); + } + + return typeface; + } + + /** + * Returns a {@link Typeface} given a font name. The font name, can be a system font family + * (like sans-serif) or a full path if the font is to be loaded from resources. + */ + public static Typeface getFont(ResourceValue value, BridgeContext context, Theme theme) { + if (value == null) { + return null; + } + + return getFont(value.getValue(), context, theme, value.isFramework()); + } + private static Drawable getNinePatchDrawable(InputStream inputStream, Density density, boolean isFramework, String cacheKey, BridgeContext context) throws IOException { // see if we still have both the chunk and the bitmap in the caches diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png Binary files differnew file mode 100644 index 000000000000..b2baa98be0a0 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml new file mode 100644 index 000000000000..b1e9206bc9f6 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<font-family xmlns:android="http://schemas.android.com/apk/res/android"> + <font android:fontStyle="normal" android:fontWeight="400" android:font="@font/testfont" /> + <font android:fontStyle="italic" android:fontWeight="400" android:font="@font/testfont2" /> +</font-family>
\ No newline at end of file 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 Binary files differnew file mode 100644 index 000000000000..285230293580 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf 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 Binary files differnew file mode 100644 index 000000000000..b7bf5b4aa8ad --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml new file mode 100644 index 000000000000..c63b211c967d --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="CONDENSED" + android:textSize="50sp" + android:fontFamily="sans-serif-condensed" + /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="CONDENSED ITALIC" + android:textSize="30sp" + android:fontFamily="sans-serif-condensed" + android:textStyle="italic" + /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="MONOSPACE" + android:textSize="50sp" + android:fontFamily="monospace"/> + + <Space + android:layout_width="wrap_content" + android:layout_height="30dp" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Custom" + android:textSize="20sp" + android:fontFamily="@font/testfont"/> + + <Space + android:layout_width="wrap_content" + android:layout_height="30dp" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Custom family" + android:textSize="20sp" + android:fontFamily="@font/testfamily"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Custom family" + android:textSize="20sp" + android:fontFamily="@font/testfamily" + android:textStyle="italic"/> + +</LinearLayout>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java index 3e5f9e074fef..67b42a7cf86d 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java @@ -35,6 +35,7 @@ import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback; import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser; import com.android.layoutlib.bridge.intensive.util.ImageUtils; import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader; +import com.android.layoutlib.bridge.intensive.util.TestAssetRepository; import com.android.layoutlib.bridge.intensive.util.TestUtils; import com.android.tools.layoutlib.java.System_Delegate; import com.android.utils.ILogger; @@ -537,6 +538,7 @@ public class RenderTestBase { configGenerator.getHardwareConfig(), resourceResolver, layoutLibCallback, 0, targetSdk, getLayoutLog()); sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true); + sessionParams.setAssetRepository(new TestAssetRepository()); return sessionParams; } } 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 73e51ecf73ce..913519ca56af 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 @@ -384,4 +384,10 @@ public class RenderTests extends RenderTestBase { strings); assertTrue(sRenderMessages.isEmpty()); } + + @Test + public void testFonts() throws ClassNotFoundException { + // TODO: styles seem to be broken in TextView + renderAndVerify("fonts_test.xml", "font_test.png"); + } } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java new file mode 100644 index 000000000000..0856ac9252bb --- /dev/null +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java @@ -0,0 +1,54 @@ +/* + * 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.layoutlib.bridge.intensive.util; + +import com.android.ide.common.rendering.api.AssetRepository; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +/** + * {@link AssetRepository} used for render tests. + */ +public class TestAssetRepository extends AssetRepository { + private static InputStream open(String path) throws FileNotFoundException { + File asset = new File(path); + if (asset.isFile()) { + return new FileInputStream(asset); + } + + return null; + } + + @Override + public boolean isSupported() { + return true; + } + + @Override + public InputStream openAsset(String path, int mode) throws IOException { + return open(path); + } + + @Override + public InputStream openNonAsset(int cookie, String path, int mode) throws IOException { + return open(path); + } +} diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk index c7f2c4137687..7611cde3f392 100644 --- a/tools/layoutlib/create/Android.mk +++ b/tools/layoutlib/create/Android.mk @@ -20,7 +20,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_JAR_MANIFEST := manifest.txt LOCAL_STATIC_JAVA_LIBRARIES := \ - asm-5.0 + asm-5.2 LOCAL_MODULE := layoutlib_create diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 94302d328313..a8582c60bc64 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -156,6 +156,7 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.Resources#getDimensionPixelOffset", "android.content.res.Resources#getDimensionPixelSize", "android.content.res.Resources#getDrawable", + "android.content.res.Resources#getFont", "android.content.res.Resources#getIntArray", "android.content.res.Resources#getInteger", "android.content.res.Resources#getLayout", diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk index 61e381d48b16..488d7d6cc18d 100644 --- a/tools/layoutlib/create/tests/Android.mk +++ b/tools/layoutlib/create/tests/Android.mk @@ -24,7 +24,7 @@ LOCAL_MODULE := layoutlib-create-tests LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := layoutlib_create junit-host -LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0 +LOCAL_STATIC_JAVA_LIBRARIES := asm-5.2 include $(BUILD_HOST_JAVA_LIBRARY) |