diff options
235 files changed, 6240 insertions, 1517 deletions
diff --git a/Android.mk b/Android.mk index ef9754941df0..8e8b95a3e8aa 100644 --- a/Android.mk +++ b/Android.mk @@ -262,8 +262,9 @@ LOCAL_SRC_FILES += \ core/java/android/os/storage/IObbActionListener.aidl \ core/java/android/security/IKeystoreService.aidl \ core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \ - core/java/android/service/autofill/IAutoFillCallback.aidl \ + core/java/android/service/autofill/IAutoFillAppCallback.aidl \ core/java/android/service/autofill/IAutoFillManagerService.aidl \ + core/java/android/service/autofill/IAutoFillServerCallback.aidl \ core/java/android/service/autofill/IAutoFillService.aidl \ core/java/android/service/carrier/ICarrierService.aidl \ core/java/android/service/carrier/ICarrierMessagingCallback.aidl \ diff --git a/api/current.txt b/api/current.txt index a4402bb7987e..9f6d358bca2f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6368,7 +6368,8 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public int getAutoFillId(); + method public android.view.autofill.AutoFillId getAutoFillId(); + method public android.view.autofill.AutoFillType getAutoFillType(); method public android.app.assist.AssistStructure.ViewNode getChildAt(int); method public int getChildCount(); method public java.lang.String getClassName(); @@ -6784,6 +6785,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); @@ -23999,11 +24001,8 @@ package android.net { method public boolean requestBandwidthUpdate(android.net.Network); method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent); - method public deprecated boolean requestRouteToHost(int, int); method public deprecated void setNetworkPreference(int); method public static deprecated boolean setProcessDefaultNetwork(android.net.Network); - method public deprecated int startUsingNetworkFeature(int, java.lang.String); - method public deprecated int stopUsingNetworkFeature(int, java.lang.String); method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback); method public void unregisterNetworkCallback(android.app.PendingIntent); field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; @@ -28580,7 +28579,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -35042,26 +35041,19 @@ package android.service.autofill { method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback); method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback); + field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS"; + field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.service.autofill.FillCallback.FillData); - } - - public static final class FillCallback.FillData { - } - - public static class FillCallback.FillData.Builder { - ctor public FillCallback.FillData.Builder(); - method public android.service.autofill.FillCallback.FillData build(); - method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String); + method public void onSuccess(android.view.autofill.FillResponse); } public final class SaveCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(int[]); + method public void onSuccess(android.view.autofill.AutoFillId[]); } } @@ -36726,6 +36718,7 @@ package android.telecom { method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; + field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_CONNECTING = 9; // 0x9 field public static final int STATE_DIALING = 1; // 0x1 @@ -39459,6 +39452,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); + method public android.text.StaticLayout.Builder setJustify(boolean); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -43067,6 +43061,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { @@ -43147,6 +43142,7 @@ package android.view { method public void addTouchables(java.util.ArrayList<android.view.View>); method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); + method public void autoFill(android.view.autofill.AutoFillValue); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); @@ -43198,8 +43194,8 @@ package android.view { method public boolean dispatchNestedPreScroll(int, int, int[], int[]); method public boolean dispatchNestedScroll(int, int, int, int, int[]); method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void dispatchProvideStructure(android.view.ViewStructure); - method public void dispatchProvideStructure(android.view.ViewStructure, int); + method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int); + method public void dispatchProvideStructure(android.view.ViewStructure); method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSetActivated(boolean); @@ -43234,6 +43230,8 @@ package android.view { method public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); + method public android.view.autofill.AutoFillType getAutoFillType(); + method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback); method public android.graphics.drawable.Drawable getBackground(); method public android.content.res.ColorStateList getBackgroundTintList(); method public android.graphics.PorterDuff.Mode getBackgroundTintMode(); @@ -43470,10 +43468,10 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void onProvideStructure(android.view.ViewStructure); - method public void onProvideStructure(android.view.ViewStructure, int); - method public deprecated void onProvideVirtualStructure(android.view.ViewStructure); - method public void onProvideVirtualStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int); + method public void onProvideStructure(android.view.ViewStructure); + method public void onProvideVirtualStructure(android.view.ViewStructure); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); @@ -43680,8 +43678,8 @@ package android.view { field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0 field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1 field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA; - field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2 - field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40 field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80 @@ -44311,6 +44309,7 @@ package android.view { method public abstract int addChildCount(int); method public abstract void asyncCommit(); method public abstract android.view.ViewStructure asyncNewChild(int); + method public abstract android.view.ViewStructure asyncNewChild(int, int); method public abstract int getChildCount(); method public abstract android.os.Bundle getExtras(); method public abstract java.lang.CharSequence getHint(); @@ -44319,9 +44318,11 @@ package android.view { method public abstract int getTextSelectionStart(); method public abstract boolean hasExtras(); method public abstract android.view.ViewStructure newChild(int); + method public abstract android.view.ViewStructure newChild(int, int); method public abstract void setAccessibilityFocused(boolean); method public abstract void setActivated(boolean); method public abstract void setAlpha(float); + method public abstract void setAutoFillType(android.view.autofill.AutoFillType); method public abstract void setCheckable(boolean); method public abstract void setChecked(boolean); method public abstract void setChildCount(int); @@ -45542,6 +45543,80 @@ package android.view.animation { } +package android.view.autofill { + + public final class AutoFillId implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR; + } + + public final class AutoFillType implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillType forList(); + method public static android.view.autofill.AutoFillType forText(int); + method public static android.view.autofill.AutoFillType forToggle(); + method public int getSubType(); + method public boolean isList(); + method public boolean isText(); + method public boolean isToggle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR; + } + + public final class AutoFillValue implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillValue forList(int); + method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence); + method public static android.view.autofill.AutoFillValue forToggle(boolean); + method public int getListValue(); + method public java.lang.CharSequence getTextValue(); + method public boolean getToggleValue(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; + } + + public final class Dataset implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.view.autofill.Dataset build(); + method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); + method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); + method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.view.autofill.FillResponse build(); + method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); + } + + public abstract class VirtualViewDelegate { + ctor public VirtualViewDelegate(); + method public abstract void autoFill(int, android.view.autofill.AutoFillValue); + } + + public static abstract class VirtualViewDelegate.Callback { + ctor public VirtualViewDelegate.Callback(); + method public void onFocusChanged(int, boolean); + method public void onNodeRemoved(int...); + method public void onValueChanged(int); + } + +} + package android.view.inputmethod { public class BaseInputConnection implements android.view.inputmethod.InputConnection { @@ -48987,6 +49062,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); + method public boolean getJustify(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -49095,6 +49171,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); + method public void setJustify(boolean); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/api/removed.txt b/api/removed.txt index d7a8bce2d00b..ab22b6ec1720 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -126,6 +126,12 @@ package android.media.tv { package android.net { + public class ConnectivityManager { + method public deprecated boolean requestRouteToHost(int, int); + method public deprecated int startUsingNetworkFeature(int, java.lang.String); + method public deprecated int stopUsingNetworkFeature(int, java.lang.String); + } + public abstract class PskKeyManager { ctor public PskKeyManager(); field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80 diff --git a/api/system-current.txt b/api/system-current.txt index 2ec564302d8c..f2b0eaeb2688 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6423,6 +6423,8 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION"; + field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI"; + field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM"; field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS"; @@ -6435,8 +6437,10 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; field public static final java.lang.String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field public static final java.lang.String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; + field public static final java.lang.String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; + field public static final java.lang.String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN"; field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL"; @@ -6571,7 +6575,8 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public int getAutoFillId(); + method public android.view.autofill.AutoFillId getAutoFillId(); + method public android.view.autofill.AutoFillType getAutoFillType(); method public android.app.assist.AssistStructure.ViewNode getChildAt(int); method public int getChildCount(); method public java.lang.String getClassName(); @@ -7098,6 +7103,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); @@ -25791,14 +25797,11 @@ package android.net { method public boolean requestBandwidthUpdate(android.net.Network); method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent); - method public deprecated boolean requestRouteToHost(int, int); method public deprecated void setNetworkPreference(int); method public static deprecated boolean setProcessDefaultNetwork(android.net.Network); method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); - method public deprecated int startUsingNetworkFeature(int, java.lang.String); method public void stopTethering(int); - method public deprecated int stopUsingNetworkFeature(int, java.lang.String); method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback); method public void unregisterNetworkCallback(android.app.PendingIntent); field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; @@ -31144,7 +31147,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -37905,26 +37908,19 @@ package android.service.autofill { method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback); method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback); + field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS"; + field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.service.autofill.FillCallback.FillData); - } - - public static final class FillCallback.FillData { - } - - public static class FillCallback.FillData.Builder { - ctor public FillCallback.FillData.Builder(); - method public android.service.autofill.FillCallback.FillData build(); - method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String); + method public void onSuccess(android.view.autofill.FillResponse); } public final class SaveCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(int[]); + method public void onSuccess(android.view.autofill.AutoFillId[]); } } @@ -39660,6 +39656,7 @@ package android.telecom { method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; + field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_CONNECTING = 9; // 0x9 field public static final int STATE_DIALING = 1; // 0x1 @@ -42677,6 +42674,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); + method public android.text.StaticLayout.Builder setJustify(boolean); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -46285,6 +46283,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { @@ -46365,6 +46364,7 @@ package android.view { method public void addTouchables(java.util.ArrayList<android.view.View>); method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); + method public void autoFill(android.view.autofill.AutoFillValue); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); @@ -46416,8 +46416,8 @@ package android.view { method public boolean dispatchNestedPreScroll(int, int, int[], int[]); method public boolean dispatchNestedScroll(int, int, int, int, int[]); method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void dispatchProvideStructure(android.view.ViewStructure); - method public void dispatchProvideStructure(android.view.ViewStructure, int); + method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int); + method public void dispatchProvideStructure(android.view.ViewStructure); method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSetActivated(boolean); @@ -46452,6 +46452,8 @@ package android.view { method public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); + method public android.view.autofill.AutoFillType getAutoFillType(); + method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback); method public android.graphics.drawable.Drawable getBackground(); method public android.content.res.ColorStateList getBackgroundTintList(); method public android.graphics.PorterDuff.Mode getBackgroundTintMode(); @@ -46688,10 +46690,10 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void onProvideStructure(android.view.ViewStructure); - method public void onProvideStructure(android.view.ViewStructure, int); - method public deprecated void onProvideVirtualStructure(android.view.ViewStructure); - method public void onProvideVirtualStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int); + method public void onProvideStructure(android.view.ViewStructure); + method public void onProvideVirtualStructure(android.view.ViewStructure); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); @@ -46898,8 +46900,8 @@ package android.view { field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0 field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1 field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA; - field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2 - field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40 field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80 @@ -47529,6 +47531,7 @@ package android.view { method public abstract int addChildCount(int); method public abstract void asyncCommit(); method public abstract android.view.ViewStructure asyncNewChild(int); + method public abstract android.view.ViewStructure asyncNewChild(int, int); method public abstract int getChildCount(); method public abstract android.os.Bundle getExtras(); method public abstract java.lang.CharSequence getHint(); @@ -47537,9 +47540,11 @@ package android.view { method public abstract int getTextSelectionStart(); method public abstract boolean hasExtras(); method public abstract android.view.ViewStructure newChild(int); + method public abstract android.view.ViewStructure newChild(int, int); method public abstract void setAccessibilityFocused(boolean); method public abstract void setActivated(boolean); method public abstract void setAlpha(float); + method public abstract void setAutoFillType(android.view.autofill.AutoFillType); method public abstract void setCheckable(boolean); method public abstract void setChecked(boolean); method public abstract void setChildCount(int); @@ -48763,6 +48768,80 @@ package android.view.animation { } +package android.view.autofill { + + public final class AutoFillId implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR; + } + + public final class AutoFillType implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillType forList(); + method public static android.view.autofill.AutoFillType forText(int); + method public static android.view.autofill.AutoFillType forToggle(); + method public int getSubType(); + method public boolean isList(); + method public boolean isText(); + method public boolean isToggle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR; + } + + public final class AutoFillValue implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillValue forList(int); + method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence); + method public static android.view.autofill.AutoFillValue forToggle(boolean); + method public int getListValue(); + method public java.lang.CharSequence getTextValue(); + method public boolean getToggleValue(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; + } + + public final class Dataset implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.view.autofill.Dataset build(); + method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); + method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); + method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.view.autofill.FillResponse build(); + method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); + } + + public abstract class VirtualViewDelegate { + ctor public VirtualViewDelegate(); + method public abstract void autoFill(int, android.view.autofill.AutoFillValue); + } + + public static abstract class VirtualViewDelegate.Callback { + ctor public VirtualViewDelegate.Callback(); + method public void onFocusChanged(int, boolean); + method public void onNodeRemoved(int...); + method public void onValueChanged(int); + } + +} + package android.view.inputmethod { public class BaseInputConnection implements android.view.inputmethod.InputConnection { @@ -50278,8 +50357,8 @@ package android.webkit { method public abstract boolean onKeyUp(int, android.view.KeyEvent); method public abstract void onMeasure(int, int); method public abstract void onOverScrolled(int, int, boolean, boolean); + method public default void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int); method public abstract void onProvideVirtualStructure(android.view.ViewStructure); - method public default void onProvideVirtualStructure(android.view.ViewStructure, int); method public abstract void onScrollChanged(int, int, int, int); method public abstract void onSizeChanged(int, int, int, int); method public abstract void onStartTemporaryDetach(); @@ -52565,6 +52644,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); + method public boolean getJustify(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -52673,6 +52753,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); + method public void setJustify(boolean); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/api/system-removed.txt b/api/system-removed.txt index 7ba88bdb9d33..1ba26f5975d1 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -124,6 +124,12 @@ package android.media.tv { package android.net { + public class ConnectivityManager { + method public deprecated boolean requestRouteToHost(int, int); + method public deprecated int startUsingNetworkFeature(int, java.lang.String); + method public deprecated int stopUsingNetworkFeature(int, java.lang.String); + } + public abstract class PskKeyManager { ctor public PskKeyManager(); field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80 diff --git a/api/test-current.txt b/api/test-current.txt index 7e05b785524e..c1ec21df688c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6390,7 +6390,8 @@ package android.app.assist { public static class AssistStructure.ViewNode { method public float getAlpha(); - method public int getAutoFillId(); + method public android.view.autofill.AutoFillId getAutoFillId(); + method public android.view.autofill.AutoFillType getAutoFillType(); method public android.app.assist.AssistStructure.ViewNode getChildAt(int); method public int getChildCount(); method public java.lang.String getClassName(); @@ -6806,6 +6807,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); @@ -24086,11 +24088,8 @@ package android.net { method public boolean requestBandwidthUpdate(android.net.Network); method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent); - method public deprecated boolean requestRouteToHost(int, int); method public deprecated void setNetworkPreference(int); method public static deprecated boolean setProcessDefaultNetwork(android.net.Network); - method public deprecated int startUsingNetworkFeature(int, java.lang.String); - method public deprecated int stopUsingNetworkFeature(int, java.lang.String); method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback); method public void unregisterNetworkCallback(android.app.PendingIntent); field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; @@ -28667,7 +28666,7 @@ package android.opengl { method public void surfaceChanged(android.view.SurfaceHolder, int, int, int); method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); - method public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public deprecated void surfaceRedrawNeeded(android.view.SurfaceHolder); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -35160,26 +35159,19 @@ package android.service.autofill { method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback); method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback); + field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS"; + field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS"; field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.service.autofill.FillCallback.FillData); - } - - public static final class FillCallback.FillData { - } - - public static class FillCallback.FillData.Builder { - ctor public FillCallback.FillData.Builder(); - method public android.service.autofill.FillCallback.FillData build(); - method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String); + method public void onSuccess(android.view.autofill.FillResponse); } public final class SaveCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(int[]); + method public void onSuccess(android.view.autofill.AutoFillId[]); } } @@ -36844,6 +36836,7 @@ package android.telecom { method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; + field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_CONNECTING = 9; // 0x9 field public static final int STATE_DIALING = 1; // 0x1 @@ -39579,6 +39572,7 @@ package android.text { method public android.text.StaticLayout.Builder setHyphenationFrequency(int); method public android.text.StaticLayout.Builder setIncludePad(boolean); method public android.text.StaticLayout.Builder setIndents(int[], int[]); + method public android.text.StaticLayout.Builder setJustify(boolean); method public android.text.StaticLayout.Builder setLineSpacing(float, float); method public android.text.StaticLayout.Builder setMaxLines(int); method public android.text.StaticLayout.Builder setText(java.lang.CharSequence); @@ -43356,6 +43350,7 @@ package android.view { public static abstract interface SurfaceHolder.Callback2 implements android.view.SurfaceHolder.Callback { method public abstract void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, java.lang.Runnable); } public class SurfaceView extends android.view.View { @@ -43436,6 +43431,7 @@ package android.view { method public void addTouchables(java.util.ArrayList<android.view.View>); method public android.view.ViewPropertyAnimator animate(); method public void announceForAccessibility(java.lang.CharSequence); + method public void autoFill(android.view.autofill.AutoFillValue); method protected boolean awakenScrollBars(); method protected boolean awakenScrollBars(int); method protected boolean awakenScrollBars(int, boolean); @@ -43487,8 +43483,8 @@ package android.view { method public boolean dispatchNestedPreScroll(int, int, int[], int[]); method public boolean dispatchNestedScroll(int, int, int, int, int[]); method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void dispatchProvideStructure(android.view.ViewStructure); - method public void dispatchProvideStructure(android.view.ViewStructure, int); + method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int); + method public void dispatchProvideStructure(android.view.ViewStructure); method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSetActivated(boolean); @@ -43523,6 +43519,8 @@ package android.view { method public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); + method public android.view.autofill.AutoFillType getAutoFillType(); + method public android.view.autofill.VirtualViewDelegate getAutoFillVirtualViewDelegate(android.view.autofill.VirtualViewDelegate.Callback); method public android.graphics.drawable.Drawable getBackground(); method public android.content.res.ColorStateList getBackgroundTintList(); method public android.graphics.PorterDuff.Mode getBackgroundTintMode(); @@ -43760,10 +43758,10 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public deprecated void onProvideStructure(android.view.ViewStructure); - method public void onProvideStructure(android.view.ViewStructure, int); - method public deprecated void onProvideVirtualStructure(android.view.ViewStructure); - method public void onProvideVirtualStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillStructure(android.view.ViewStructure, int); + method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int); + method public void onProvideStructure(android.view.ViewStructure); + method public void onProvideVirtualStructure(android.view.ViewStructure); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); @@ -43970,8 +43968,8 @@ package android.view { field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0 field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1 field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA; - field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2 - field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1 + field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2 field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100 field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40 field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80 @@ -44605,6 +44603,7 @@ package android.view { method public abstract int addChildCount(int); method public abstract void asyncCommit(); method public abstract android.view.ViewStructure asyncNewChild(int); + method public abstract android.view.ViewStructure asyncNewChild(int, int); method public abstract int getChildCount(); method public abstract android.os.Bundle getExtras(); method public abstract java.lang.CharSequence getHint(); @@ -44613,9 +44612,11 @@ package android.view { method public abstract int getTextSelectionStart(); method public abstract boolean hasExtras(); method public abstract android.view.ViewStructure newChild(int); + method public abstract android.view.ViewStructure newChild(int, int); method public abstract void setAccessibilityFocused(boolean); method public abstract void setActivated(boolean); method public abstract void setAlpha(float); + method public abstract void setAutoFillType(android.view.autofill.AutoFillType); method public abstract void setCheckable(boolean); method public abstract void setChecked(boolean); method public abstract void setChildCount(int); @@ -45838,6 +45839,80 @@ package android.view.animation { } +package android.view.autofill { + + public final class AutoFillId implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR; + } + + public final class AutoFillType implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillType forList(); + method public static android.view.autofill.AutoFillType forText(int); + method public static android.view.autofill.AutoFillType forToggle(); + method public int getSubType(); + method public boolean isList(); + method public boolean isText(); + method public boolean isToggle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillType> CREATOR; + } + + public final class AutoFillValue implements android.os.Parcelable { + method public int describeContents(); + method public static android.view.autofill.AutoFillValue forList(int); + method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence); + method public static android.view.autofill.AutoFillValue forToggle(boolean); + method public int getListValue(); + method public java.lang.CharSequence getTextValue(); + method public boolean getToggleValue(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; + } + + public final class Dataset implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.view.autofill.Dataset build(); + method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); + method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); + method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.view.autofill.FillResponse build(); + method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); + } + + public abstract class VirtualViewDelegate { + ctor public VirtualViewDelegate(); + method public abstract void autoFill(int, android.view.autofill.AutoFillValue); + } + + public static abstract class VirtualViewDelegate.Callback { + ctor public VirtualViewDelegate.Callback(); + method public void onFocusChanged(int, boolean); + method public void onNodeRemoved(int...); + method public void onValueChanged(int); + } + +} + package android.view.inputmethod { public class BaseInputConnection implements android.view.inputmethod.InputConnection { @@ -49289,6 +49364,7 @@ package android.widget { method public boolean getIncludeFontPadding(); method public android.os.Bundle getInputExtras(boolean); method public int getInputType(); + method public boolean getJustify(); method public final android.text.method.KeyListener getKeyListener(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); @@ -49397,6 +49473,7 @@ package android.widget { method public void setIncludeFontPadding(boolean); method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public void setInputType(int); + method public void setJustify(boolean); method public void setKeyListener(android.text.method.KeyListener); method public void setLetterSpacing(float); method public void setLineSpacing(float, float); diff --git a/api/test-removed.txt b/api/test-removed.txt index d7a8bce2d00b..ab22b6ec1720 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -126,6 +126,12 @@ package android.media.tv { package android.net { + public class ConnectivityManager { + method public deprecated boolean requestRouteToHost(int, int); + method public deprecated int startUsingNetworkFeature(int, java.lang.String); + method public deprecated int stopUsingNetworkFeature(int, java.lang.String); + } + public abstract class PskKeyManager { ctor public PskKeyManager(); field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 0d9e8a0afad2..87e54166b3a1 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -70,9 +70,8 @@ import android.os.ServiceManager.ServiceNotFoundException; import android.os.StrictMode; import android.os.SystemProperties; import android.os.UserHandle; -import android.service.autofill.FillableInputField; import android.service.autofill.AutoFillService; -import android.service.autofill.IAutoFillCallback; +import android.service.autofill.IAutoFillAppCallback; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.TextAssistant; @@ -115,6 +114,11 @@ import android.view.Window.WindowControllerCallback; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; +import android.view.autofill.VirtualViewDelegate; +import android.view.autofill.Dataset; +import android.view.autofill.DatasetField; +import android.view.autofill.AutoFillId; +import android.view.autofill.FillResponse; import android.widget.AdapterView; import android.widget.EditText; import android.widget.Toast; @@ -130,6 +134,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -697,6 +702,9 @@ public class Activity extends ContextThemeWrapper private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; + // TODO(b/33197203): set to false once stable + private static final boolean DEBUG_AUTO_FILL = true; + /** Standard activity result: operation canceled. */ public static final int RESULT_CANCELED = 0; /** Standard activity result: operation succeeded. */ @@ -847,7 +855,10 @@ public class Activity extends ContextThemeWrapper private boolean mEatKeyUpEvent; @GuardedBy("this") - private IAutoFillCallback mAutoFillCallback; + private WeakReference<IAutoFillAppCallback> mAutoFillCallback; + + @GuardedBy("this") + private VirtualViewDelegate.Callback mAutoFillDelegateCallback; private static native String getDlWarning(); @@ -1718,47 +1729,73 @@ public class Activity extends ContextThemeWrapper } /** - * Lazily gets the {@code IAutoFillCallback} for this activitity. + * Lazily sets the {@link #mAutoFillDelegateCallback}. + */ + private void setAutoFillDelegateCallback() { + synchronized (this) { + if (mAutoFillDelegateCallback == null) { + mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() { + // TODO(b/33197203): implement + }; + } + } + } + + /** + * Lazily gets the {@link IAutoFillAppCallback} for this activitity. * * <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields. */ - IAutoFillCallback getAutoFillCallback() { + WeakReference<IAutoFillAppCallback> getAutoFillCallback() { synchronized (this) { if (mAutoFillCallback == null) { - mAutoFillCallback = new IAutoFillCallback.Stub() { + final IAutoFillAppCallback cb = new IAutoFillAppCallback.Stub() { @Override - public void autofill(@SuppressWarnings("rawtypes") List fields) - throws RemoteException { + public void autoFill(Dataset dataset) throws RemoteException { + // TODO(b/33197203): must keep the dataset so subsequent calls pass the same + // dataset.extras to service runOnUiThread(() -> { final View root = getWindow().getDecorView().getRootView(); - for (Object field : fields) { - if (!(field instanceof FillableInputField)) { - Slog.w(TAG, "autofill(): invalid type " + field.getClass()); + for (DatasetField field : dataset.getFields()) { + final AutoFillId id = field.getId(); + if (id == null) { + Log.w(TAG, "autoFill(): null id on " + field); continue; } - FillableInputField autoFillField = (FillableInputField) field; - final int viewId = autoFillField.getId(); + final int viewId = id.getViewId(); final View view = root.findViewByAccessibilityIdTraversal(viewId); - // TODO(b/33197203): should handle other types of view as well, but - // that will require: - // - a new interface like AutoFillable - // - a way for the views to define the type of the autofield value - if ((view instanceof EditText)) { - ((EditText) view).setText(autoFillField.getValue()); + if (view == null) { + Log.w(TAG, "autoFill(): no View with id " + viewId); + continue; } - } - }); - } - @Override - public void showError(String message) { - runOnUiThread(() -> { - // TODO(b/33197203): temporary show a toast until it uses the Snack bar. - Toast.makeText(Activity.this, "Auto-fill request failed: " + message, - Toast.LENGTH_LONG).show(); + // TODO(b/33197203): handle protected value (like credit card) + if (id.isVirtual()) { + // Delegate virtual fields to provider. + setAutoFillDelegateCallback(); + final VirtualViewDelegate mgr = view + .getAutoFillVirtualViewDelegate( + mAutoFillDelegateCallback); + if (mgr == null) { + Log.w(TAG, "autoFill(): cannot fill virtual " + id + + "; no auto-fill provider for view " + + view.getClass()); + continue; + } + if (DEBUG_AUTO_FILL) { + Log.d(TAG, "autoFill(): delegating " + id + + " to virtual manager " + mgr); + } + mgr.autoFill(id.getVirtualChildId(), field.getValue()); + } else { + // Handle non-virtual fields itself. + view.autoFill(field.getValue()); + } + } }); } }; + mAutoFillCallback = new WeakReference<IAutoFillAppCallback>(cb); } } return mAutoFillCallback; @@ -6096,7 +6133,7 @@ public class Activity extends ContextThemeWrapper if (mAutoFillCallback != null) { writer.print(prefix); writer.print("mAutoFillCallback: " ); - writer.println(mAutoFillCallback); + writer.println(mAutoFillCallback.get()); } mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index cd50c4dbec15..d362b012c000 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -483,6 +483,12 @@ public class ActivityManager { /** @hide requestType for assist context: generate full AssistStructure. */ public static final int ASSIST_CONTEXT_FULL = 1; + /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */ + public static final int ASSIST_CONTEXT_AUTO_FILL = 2; + + /** @hide requestType for assist context: generate full AssistStructure for auto-fill save. */ + public static final int ASSIST_CONTEXT_AUTO_FILL_SAVE = 3; + /** @hide Flag for registerUidObserver: report changes in process state. */ public static final int UID_OBSERVER_PROCSTATE = 1<<0; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e34fabced517..e3bbc92ad6c5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -89,7 +89,7 @@ import android.provider.Settings; import android.security.NetworkSecurityPolicy; import android.security.net.config.NetworkSecurityConfigProvider; import android.service.autofill.AutoFillService; -import android.service.autofill.IAutoFillCallback; +import android.service.autofill.IAutoFillAppCallback; import android.service.voice.VoiceInteractionSession; import android.util.AndroidRuntimeException; import android.util.ArrayMap; @@ -2884,9 +2884,8 @@ public final class ActivityThread { // - it does not call onProvideAssistData() // - it needs an IAutoFillCallback // - it sets the flags so views can provide autofill-specific data (such as passwords) - boolean forAutoFill = (cmd.flags - & (View.ASSIST_FLAG_SANITIZED_TEXT - | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0; + boolean forAutoFill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL + || cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL_SAVE; // TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions if (mLastSessionId != cmd.sessionId) { @@ -2910,22 +2909,23 @@ public final class ActivityThread { if (!forAutoFill) { r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); r.activity.onProvideAssistData(data); + referrer = r.activity.onProvideReferrer(); } - referrer = r.activity.onProvideReferrer(); if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) { structure = new AssistStructure(r.activity, cmd.flags); Intent activityIntent = r.activity.getIntent(); - if (cmd.flags > 0) { + if (forAutoFill) { data.putInt(VoiceInteractionSession.KEY_FLAGS, cmd.flags); } + boolean addAutoFillCallback = false; // TODO(b/33197203): re-evaluate conditions below for auto-fill. In particular, // FLAG_SECURE might be allowed on AUTO_FILL but not on AUTO_FILL_SAVE) - if (activityIntent != null && (r.window == null || + boolean notSecure = r.window == null || (r.window.getAttributes().flags - & WindowManager.LayoutParams.FLAG_SECURE) == 0)) { + & WindowManager.LayoutParams.FLAG_SECURE) == 0; + if (activityIntent != null && notSecure) { if (forAutoFill) { - IAutoFillCallback autoFillCallback = r.activity.getAutoFillCallback(); - data.putBinder(AutoFillService.KEY_CALLBACK, autoFillCallback.asBinder()); + addAutoFillCallback = true; } else { Intent intent = new Intent(activityIntent); intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION @@ -2936,10 +2936,21 @@ public final class ActivityThread { } else { if (!forAutoFill) { content.setDefaultIntent(new Intent()); + } else { + // activityIntent is unlikely to be null, but if it is, we should still + // set the auto-fill callback. + addAutoFillCallback = notSecure; } } if (!forAutoFill) { r.activity.onProvideAssistContent(content); + } else if (addAutoFillCallback) { + IAutoFillAppCallback cb = r.activity.getAutoFillCallback().get(); + if (cb != null) { + data.putBinder(AutoFillService.KEY_CALLBACK, cb.asBinder()); + } else { + Slog.w(TAG, "handleRequestAssistContextExtras(): callback was GCed"); + } } } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 21854d3cfa10..a2d9c45035d3 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -586,7 +586,7 @@ interface IActivityManager { void unregisterTaskStackListener(ITaskStackListener listener); void moveStackToDisplay(int stackId, int displayId); boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras, - in IBinder activityToken, int flags); + int resultCode, in IBinder activityToken, int flags); void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback); int restartUserInBackground(int userId); diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index e454ae170e28..ef997c90166e 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -95,4 +95,11 @@ oneway interface ITaskStackListener { * perform relevant animations before the window disappears. */ void onTaskRemovalStarted(int taskId); + + /** + * Called when the task has been put in a locked state because one or more of the + * activities inside it belong to a managed profile user, and that user has just + * been locked. + */ + void onTaskProfileLocked(int taskId, int userId); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 063955224e22..ad5e69b5cbd4 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -74,4 +74,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) throws RemoteException { } + + @Override + public void onTaskProfileLocked(int taskId, int userId) { + } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a56ff5e15e9b..29633aa4fbdd 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -279,6 +279,8 @@ public class DevicePolicyManager { * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li> * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li> * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li> + * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL}, optional</li> + * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI}, optional</li> * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li> * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li> * <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li> @@ -290,6 +292,8 @@ public class DevicePolicyManager { * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li> * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li> * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li> + * <li>{@link #EXTRA_PROVISIONING_SUPPORT_URL}, optional</li> + * <li>{@link #EXTRA_PROVISIONING_ORGANIZATION_NAME}, optional</li> * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li></ul> * * @hide @@ -688,6 +692,67 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION"; /** + * A String extra holding the localized name of the organization under management. + * + * The name is displayed only during provisioning. + * + * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = + "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; + + /** + * A String extra holding a url to the website of the device's provider. The website can be + * opened in a browser during provisioning. + * + * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_SUPPORT_URL = + "android.app.extra.PROVISIONING_SUPPORT_URL"; + + /** + * A String extra holding the localized name of the device admin package. It should be the same + * as the app label of the package. + * + * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = + "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; + + /** + * A {@link Uri} extra pointing to the app icon of device admin package. This image will be + * shown during the provisioning. + * <h5>The following URI schemes are accepted:</h5> + * <ul> + * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li> + * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li> + * </ul> + * + * <p> It is the responsibility of the caller to provide an image with a reasonable + * pixel density for the device. + * + * <p> If a content: URI is passed, the intent should have the flag + * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the + * {@link android.content.ClipData} of the intent too. + * + * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} + * + * @hide + */ + @SystemApi + public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = + "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI"; + + /** * An int extra holding a minimum required version code for the device admin package. If the * device admin is already installed on the device, it will only be re-downloaded from * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the @@ -808,7 +873,7 @@ public class DevicePolicyManager { * </ul> * * <p> It is the responsibility of the caller to provide an image with a reasonable - * pixed density for the device. + * pixel density for the device. * * <p> If a content: URI is passed, the intent should have the flag * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 1988e4265fb0..b94264e1ac5d 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -21,6 +21,8 @@ import android.view.ViewStructure; import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillId; import java.util.ArrayList; @@ -411,25 +413,30 @@ public class AssistStructure implements Parcelable { mTitle = root.getTitle(); mDisplayId = root.getDisplayId(); mRoot = new ViewNode(); + + // Must explicitly call the proper method based on flags since we don't know which + // method (if any) was overridden by the View subclass. + boolean forAutoFill = (flags + & (View.AUTO_FILL_FLAG_TYPE_FILL + | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; + ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); 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. - // Must explicitly set which method to calls since View subclasses might - // have implemented the deprecated method. - if (flags == 0) { - view.onProvideStructure(builder); + if (forAutoFill) { + view.onProvideAutoFillStructure(builder, flags); } else { - view.onProvideStructure(builder, flags); + view.onProvideStructure(builder); } builder.setAssistBlocked(true); return; } - if (flags == 0) { - view.dispatchProvideStructure(builder); + if (forAutoFill) { + view.dispatchProvideAutoFillStructure(builder, flags); } else { - view.dispatchProvideStructure(builder, flags); + view.dispatchProvideStructure(builder); } } @@ -526,7 +533,10 @@ public class AssistStructure implements Parcelable { String mIdPackage; String mIdType; String mIdEntry; - int mAutoFillId = View.NO_ID; + // TODO(b/33197203): once we have more flags, it might be better to store the individual + // fields (viewId and childId) of the field. + AutoFillId mAutoFillId; + AutoFillType mAutoFillType; int mX; int mY; int mScrollX; @@ -551,7 +561,11 @@ public class AssistStructure implements Parcelable { static final int FLAGS_ACTIVATED = 0x00002000; static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000; - static final int FLAGS_HAS_AUTO_FILL_ID = 0x80000000; + // TODO(b/33197203): auto-fill data is made of many fields and ideally we should verify + // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd + // need to create a 'flags2' or 'autoFillFlags' field and add these flags there. + // So, to keep thinkg simpler for now, let's just use on flag for all of them... + static final int FLAGS_HAS_AUTO_FILL_DATA = 0x80000000; static final int FLAGS_HAS_MATRIX = 0x40000000; static final int FLAGS_HAS_ALPHA = 0x20000000; static final int FLAGS_HAS_ELEVATION = 0x10000000; @@ -595,8 +609,9 @@ public class AssistStructure implements Parcelable { } } } - if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) { - mAutoFillId = in.readInt(); + if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) { + mAutoFillId = in.readParcelable(null); + mAutoFillType = in.readParcelable(null); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -653,8 +668,8 @@ public class AssistStructure implements Parcelable { if (mId != View.NO_ID) { flags |= FLAGS_HAS_ID; } - if (mAutoFillId != View.NO_ID) { - flags |= FLAGS_HAS_AUTO_FILL_ID; + if (mAutoFillId != null) { + flags |= FLAGS_HAS_AUTO_FILL_DATA; } if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0 || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) { @@ -700,8 +715,9 @@ public class AssistStructure implements Parcelable { } } } - if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) { - out.writeInt(mAutoFillId); + if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) { + out.writeParcelable(mAutoFillId, 0); + out.writeParcelable(mAutoFillType, 0); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -773,16 +789,26 @@ public class AssistStructure implements Parcelable { } /** - * Returns the id that can be used to auto-fill the view. + * Gets the id that can be used to auto-fill the view contents. * * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not * for assist. */ - public int getAutoFillId() { + public AutoFillId getAutoFillId() { return mAutoFillId; } /** + * Gets the the type of value that can be used to auto-fill the view contents. + * + * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not + * for assist. + */ + public AutoFillType getAutoFillType() { + return mAutoFillType; + } + + /** * Returns the left edge of this view, in pixels, relative to the left edge of its parent. */ public int getLeft() { @@ -1318,17 +1344,23 @@ public class AssistStructure implements Parcelable { return mNode.mChildren != null ? mNode.mChildren.length : 0; } - @Override - public ViewStructure newChild(int index) { + private void setAutoFillId(ViewNode child, boolean forAutoFill, int virtualId) { + if (forAutoFill) { + child.mAutoFillId = new AutoFillId(mNode.mAutoFillId, virtualId); + } + } + + private ViewStructure newChild(int index, boolean forAutoFill, int virtualId) { ViewNode node = new ViewNode(); + setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; return new ViewNodeBuilder(mAssist, node, false); } - @Override - public ViewStructure asyncNewChild(int index) { + private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) { synchronized (mAssist) { ViewNode node = new ViewNode(); + setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); mAssist.mPendingAsyncChildren.add(builder); @@ -1337,6 +1369,26 @@ public class AssistStructure implements Parcelable { } @Override + public ViewStructure newChild(int index) { + return newChild(index, false, 0); + } + + @Override + public ViewStructure newChild(int index, int virtualId) { + return newChild(index, true, virtualId); + } + + @Override + public ViewStructure asyncNewChild(int index) { + return asyncNewChild(index, false, 0); + } + + @Override + public ViewStructure asyncNewChild(int index, int virtualId) { + return asyncNewChild(index, true, virtualId); + } + + @Override public void asyncCommit() { synchronized (mAssist) { if (!mAsync) { @@ -1356,9 +1408,20 @@ public class AssistStructure implements Parcelable { } @Override - public void setAutoFillId(int autoFillId) { - mNode.mAutoFillId = autoFillId; + public void setAutoFillId(int viewId) { + mNode.mAutoFillId = new AutoFillId(viewId); + } + + @Override + public AutoFillId getAutoFillId() { + return mNode.mAutoFillId; } + + @Override + public void setAutoFillType(AutoFillType type) { + mNode.mAutoFillType = type; + } + } /** @hide */ diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 4c8360f6c345..ef3b1c50ffd3 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -347,11 +347,11 @@ public class AppWidgetHostView extends FrameLayout { } /** - * Sets an executor which can be used for asynchronously inflating and applying the remoteviews. - * @see {@link RemoteViews#applyAsync(Context, ViewGroup, RemoteViews.OnViewAppliedListener, Executor)} + * Sets an executor which can be used for asynchronously inflating. CPU intensive tasks like + * view inflation or loading images will be performed on the executor. The updates will still + * be applied on the UI thread. * * @param executor the executor to use or null. - * @hide */ public void setAsyncExecutor(Executor executor) { if (mLastExecutionSignal != null) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 318968158559..31e779f0dc4b 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -1094,8 +1094,9 @@ public class AppWidgetManager { * <p>Only apps with a foreground activity or a foreground service can call it. Otherwise * it'll throw {@link IllegalStateException}. * - * <p>When an app calls this API when a previous request is still waiting for a response, - * the previous request will be canceled. + * <p>It's up to the launcher how to handle previous pending requests when the same package + * calls this API multiple times in a row. It may ignore the previous requests, + * for example. * * @param provider The {@link ComponentName} for the {@link * android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget. diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index b68693880acf..98a5341b3025 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -243,7 +243,7 @@ public final class BluetoothSocket implements Closeable { } as.mPfd = new ParcelFileDescriptor(fds[0]); - as.mSocket = new LocalSocket(fds[0]); + as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; @@ -367,7 +367,7 @@ public final class BluetoothSocket implements Closeable { if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mPfd == null) throw new IOException("bt socket connect failed"); FileDescriptor fd = mPfd.getFileDescriptor(); - mSocket = new LocalSocket(fd); + mSocket = LocalSocket.createConnectedLocalSocket(fd); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } @@ -416,9 +416,9 @@ public final class BluetoothSocket implements Closeable { if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - if (DBG) Log.d(TAG, "bindListen(), new LocalSocket "); - mSocket = new LocalSocket(fd); - if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket"); + mSocket = LocalSocket.createConnectedLocalSocket(fd); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()"); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 4b5b995cd218..afd757877603 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1200,7 +1200,7 @@ public class LauncherApps { /** * Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been - * called, and it has not been canceled. + * called yet. */ public boolean isValid() { try { diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index c8fb3d174fe0..385340000d83 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -846,8 +846,9 @@ public class ShortcutManager { * <p>Only apps with a foreground activity or a foreground service can call it. Otherwise * it'll throw {@link IllegalStateException}. * - * <p>When an app calls this API when a previous request is still waiting for a response, - * the previous request will be canceled. + * <p>It's up to the launcher how to handle previous pending requests when the same package + * calls this API multiple times in a row. It may ignore the previous requests, + * for example. * * @param shortcut New shortcut to pin. If an app wants to pin an existing (either dynamic * or manifest) shortcut, then it only needs to have an ID, and other fields don't have to diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 13ba6cc37d22..aea125835748 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -167,6 +167,8 @@ public abstract class DisplayManagerInternal { public static final int POLICY_DIM = 2; // Policy: Make the screen bright as usual. public static final int POLICY_BRIGHT = 3; + // Policy: Keep the screen and display optimized for VR mode. + public static final int POLICY_VR = 4; // The basic overall policy to apply: off, doze, dim or bright. public int policy; @@ -233,6 +235,10 @@ public abstract class DisplayManagerInternal { return policy == POLICY_BRIGHT || policy == POLICY_DIM; } + public boolean isVr() { + return policy == POLICY_VR; + } + public void copyFrom(DisplayPowerRequest other) { policy = other.policy; useProximitySensor = other.useProximitySensor; @@ -301,6 +307,8 @@ public abstract class DisplayManagerInternal { return "DIM"; case POLICY_BRIGHT: return "BRIGHT"; + case POLICY_VR: + return "VR"; default: return Integer.toString(policy); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d1d5f40739db..ac0c0dc5a916 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1163,6 +1163,7 @@ public class ConnectivityManager { * {@link #requestNetwork(NetworkRequest, NetworkCallback)} API. * In {@link VERSION_CODES#M}, and above, this method is unsupported and will * throw {@code UnsupportedOperationException} if called. + * @removed */ @Deprecated public int startUsingNetworkFeature(int networkType, String feature) { @@ -1219,6 +1220,7 @@ public class ConnectivityManager { * {@link #unregisterNetworkCallback(NetworkCallback)} API. * In {@link VERSION_CODES#M}, and above, this method is unsupported and will * throw {@code UnsupportedOperationException} if called. + * @removed */ @Deprecated public int stopUsingNetworkFeature(int networkType, String feature) { @@ -1633,6 +1635,7 @@ public class ConnectivityManager { * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API. * In {@link VERSION_CODES#M}, and above, this method is unsupported and will * throw {@code UnsupportedOperationException} if called. + * @removed */ @Deprecated public boolean requestRouteToHost(int networkType, int hostAddress) { diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl index 932f03116f15..9573953e6e13 100644 --- a/core/java/android/net/INetworkScoreService.aidl +++ b/core/java/android/net/INetworkScoreService.aidl @@ -100,4 +100,21 @@ interface INetworkScoreService * @hide */ boolean requestScores(in NetworkKey[] networks); + + /** + * Determine whether the application with the given UID is the enabled scorer. + * + * @param callingUid the UID to check + * @return true if the provided UID is the active scorer, false otherwise. + * @hide + */ + boolean isCallerActiveScorer(int callingUid); + + /** + * Obtain the package name of the current active network scorer. + * + * @return the full package name of the current active scorer, or null if there is no active + * scorer. + */ + String getActiveScorerPackage(); } diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java index e1eaf00d0f20..3fcde33071e9 100644 --- a/core/java/android/net/LocalServerSocket.java +++ b/core/java/android/net/LocalServerSocket.java @@ -89,7 +89,7 @@ public class LocalServerSocket { impl.accept(acceptedImpl); - return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN); + return LocalSocket.createLocalSocketForAccept(acceptedImpl); } /** diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java index d9ad74beb016..8afa1ed3e4a5 100644 --- a/core/java/android/net/LocalSocket.java +++ b/core/java/android/net/LocalSocket.java @@ -31,6 +31,7 @@ import java.net.SocketOptions; public class LocalSocket implements Closeable { private final LocalSocketImpl impl; + /** false if impl.create() needs to be called */ private volatile boolean implCreated; private LocalSocketAddress localAddress; private boolean isBound; @@ -61,19 +62,6 @@ public class LocalSocket implements Closeable { */ public LocalSocket(int sockType) { this(new LocalSocketImpl(), sockType); - isBound = false; - isConnected = false; - } - - /** - * Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor. - * @hide - */ - public LocalSocket(FileDescriptor fd) throws IOException { - this(new LocalSocketImpl(fd), SOCKET_UNKNOWN); - isBound = true; - isConnected = true; - implCreated = true; } private LocalSocket(LocalSocketImpl impl, int sockType) { @@ -84,9 +72,27 @@ public class LocalSocket implements Closeable { } /** + * Creates a LocalSocket instances using the FileDescriptor for an already-connected + * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller: + * closing the LocalSocket will not close it. + * + * @hide - used by BluetoothSocket. + */ + public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) { + return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN); + } + + /** * for use with LocalServerSocket.accept() */ - static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) { + static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) { + return createConnectedLocalSocket(impl, SOCKET_UNKNOWN); + } + + /** + * Creates a LocalSocket from an existing LocalSocketImpl that is already connected. + */ + private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) { LocalSocket socket = new LocalSocket(impl, sockType); socket.isConnected = true; socket.isBound = true; diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index d8f782115cf8..05c8afb358b0 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -218,7 +218,7 @@ class LocalSocketImpl * * @param fd non-null; bound file descriptor */ - /*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException + /*package*/ LocalSocketImpl(FileDescriptor fd) { this.fd = fd; } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 717ac70ab430..2870dd6c5778 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -168,11 +168,11 @@ public class NetworkScoreManager { * scorer. */ public String getActiveScorerPackage() { - NetworkScorerAppData app = new NetworkScorerAppManager(mContext).getActiveScorer(); - if (app == null) { - return null; + try { + return mService.getActiveScorerPackage(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - return app.packageName; } /** @@ -339,4 +339,19 @@ public class NetworkScoreManager { throw e.rethrowFromSystemServer(); } } + + /** + * Determine whether the application with the given UID is the enabled scorer. + * + * @param callingUid the UID to check + * @return true if the provided UID is the active scorer, false otherwise. + * @hide + */ + public boolean isCallerActiveScorer(int callingUid) { + try { + return mService.isCallerActiveScorer(callingUid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java index ec0bb25d28eb..23d5af55641c 100644 --- a/core/java/android/net/NetworkScorerAppManager.java +++ b/core/java/android/net/NetworkScorerAppManager.java @@ -16,7 +16,6 @@ package android.net; -import android.Manifest; import android.Manifest.permission; import android.annotation.Nullable; import android.content.ContentResolver; @@ -28,7 +27,9 @@ import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; + import com.android.internal.R; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -226,6 +227,7 @@ public class NetworkScorerAppManager { } /** Determine whether the application with the given UID is the enabled scorer. */ + @Deprecated // Use NetworkScoreManager.isCallerActiveScorer() public boolean isCallerActiveScorer(int callingUid) { NetworkScorerAppData defaultApp = getActiveScorer(); if (defaultApp == null) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 9d04929b2c8f..dff0a287e25c 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -49,23 +49,23 @@ import android.util.Log; * <i>These levels are mutually exclusive - you may only specify one of them.</i> * * <table> - * <tr><th>Flag Value</th> + * <tr><th>Flag Value</th> * <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr> * * <tr><td>{@link #PARTIAL_WAKE_LOCK}</td> - * <td>On*</td> <td>Off</td> <td>Off</td> + * <td>On*</td> <td>Off</td> <td>Off</td> * </tr> - * + * * <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td> - * <td>On</td> <td>Dim</td> <td>Off</td> + * <td>On</td> <td>Dim</td> <td>Off</td> * </tr> * * <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td> - * <td>On</td> <td>Bright</td> <td>Off</td> + * <td>On</td> <td>Bright</td> <td>Off</td> * </tr> - * + * * <tr><td>{@link #FULL_WAKE_LOCK}</td> - * <td>On</td> <td>Bright</td> <td>Bright</td> + * <td>On</td> <td>Bright</td> <td>Bright</td> * </tr> * </table> * </p><p> @@ -85,13 +85,13 @@ import android.util.Log; * the illumination to remain on once it turns on (e.g. from user activity). This flag * will force the screen and/or keyboard to turn on immediately, when the WakeLock is * acquired. A typical use would be for notifications which are important for the user to - * see immediately.</td> + * see immediately.</td> * </tr> - * + * * <tr><td>{@link #ON_AFTER_RELEASE}</td> * <td>If this flag is set, the user activity timer will be reset when the WakeLock is - * released, causing the illumination to remain on a bit longer. This can be used to - * reduce flicker if you are cycling between wake lock conditions.</td> + * released, causing the illumination to remain on a bit longer. This can be used to + * reduce flicker if you are cycling between wake lock conditions.</td> * </tr> * </table> * <p> @@ -479,6 +479,35 @@ public final class PowerManager { } /** + * Gets the minimum supported screen brightness setting for VR Mode. + * @hide + */ + public int getMinimumScreenBrightnessForVrSetting() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_screenBrightnessForVrSettingMinimum); + } + + /** + * Gets the maximum supported screen brightness setting for VR Mode. + * The screen may be allowed to become dimmer than this value but + * this is the maximum value that can be set by the user. + * @hide + */ + public int getMaximumScreenBrightnessForVrSetting() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_screenBrightnessForVrSettingMaximum); + } + + /** + * Gets the default screen brightness for VR setting. + * @hide + */ + public int getDefaultScreenBrightnessForVrSetting() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault); + } + + /** * Returns true if the twilight service should be used to adjust screen brightness * policy. This setting is experimental and disabled by default. * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cede9eb5e675..2d4a9e93af7e 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2889,6 +2889,15 @@ public final class Settings { new InclusiveIntegerRangeValidator(0, 255); /** + * The screen backlight brightness between 0 and 255. + * @hide + */ + public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr"; + + private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 255); + + /** * Control whether to enable automatic brightness mode. */ public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; @@ -3882,6 +3891,7 @@ public final class Settings { VALIDATORS.put(DIM_SCREEN, DIM_SCREEN_VALIDATOR); VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR); VALIDATORS.put(SCREEN_BRIGHTNESS, SCREEN_BRIGHTNESS_VALIDATOR); + VALIDATORS.put(SCREEN_BRIGHTNESS_FOR_VR, SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR); VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR); VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR); VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR); diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java index a7941c7ebeeb..c2e980c5c277 100644 --- a/core/java/android/service/autofill/AutoFillService.java +++ b/core/java/android/service/autofill/AutoFillService.java @@ -17,8 +17,8 @@ package android.service.autofill; import static android.service.voice.VoiceInteractionSession.KEY_FLAGS; import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE; -import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT; -import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT; +import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL; +import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; import android.annotation.SdkConstant; import android.app.Activity; @@ -32,12 +32,15 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.view.autofill.AutoFillId; +import android.view.autofill.FillResponse; import com.android.internal.os.HandlerCaller; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; -// TODO(b/33197203): improve javadoc (class and methods) +// TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the +// life-cycle (and how state could be maintained on server-side) is well documented. /** * Top-level service of the current auto-fill service for a given user. @@ -58,40 +61,50 @@ public abstract class AutoFillService extends Service { @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; - // Bundle keys. - /** @hide */ - public static final String KEY_CALLBACK = "callback"; + // Internal bundle keys. + /** @hide */ public static final String KEY_CALLBACK = "callback"; + /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids"; + + // Prefix for public bundle keys. + private static final String KEY_PREFIX = "android.service.autofill.extra."; + + /** + * Key of the {@link Bundle} passed to methods such as + * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)} + * containing the extras set by + * {@link android.view.autofill.FillResponse.Builder#setExtras(Bundle)}. + */ + public static final String EXTRA_RESPONSE_EXTRAS = KEY_PREFIX + "RESPONSE_EXTRAS"; + + /** + * Key of the {@link Bundle} passed to methods such as + * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)} + * containing the extras set by + * {@link android.view.autofill.Dataset.Builder#setExtras(Bundle)}. + */ + public static final String EXTRA_DATASET_EXTRAS = KEY_PREFIX + "DATASET_EXTRAS"; // Handler messages. private static final int MSG_CONNECT = 1; private static final int MSG_AUTO_FILL_ACTIVITY = 2; private static final int MSG_DISCONNECT = 3; - private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { - @Override - public void send(int resultCode, Bundle resultData) throws RemoteException { - final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE); - final IBinder binder = resultData.getBinder(KEY_CALLBACK); - final int flags = resultData.getInt(KEY_FLAGS, 0); + private final IAutoFillService mInterface = new IAutoFillService.Stub() { + @Override + public void autoFill(AssistStructure structure, IAutoFillServerCallback callback, + Bundle extras, int flags) { mHandlerCaller - .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, binder).sendToTarget(); + .obtainMessageIOOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, extras, callback) + .sendToTarget(); } - }; - - private final IAutoFillService mInterface = new IAutoFillService.Stub() { @Override public void onConnected() { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CONNECT)); } @Override - public IResultReceiver getAssistReceiver() { - return mAssistReceiver; - } - - @Override public void onDisconnected() { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DISCONNECT)); } @@ -107,10 +120,11 @@ public abstract class AutoFillService extends Service { break; } case MSG_AUTO_FILL_ACTIVITY: { final SomeArgs args = (SomeArgs) msg.obj; - final AssistStructure structure = (AssistStructure) args.arg1; - final IBinder binder = (IBinder) args.arg2; final int flags = msg.arg1; - requestAutoFill(structure, flags, binder); + final AssistStructure structure = (AssistStructure) args.arg1; + final Bundle extras = (Bundle) args.arg2; + final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg3; + requestAutoFill(callback, structure, extras, flags); break; } case MSG_DISCONNECT: { onDisconnected(); @@ -146,7 +160,7 @@ public abstract class AutoFillService extends Service { } /** - * Called when the Android System connects to service. + * Called when the Android system connects to service. * * <p>You should generally do initialization here rather than in {@link #onCreate}. */ @@ -155,12 +169,19 @@ public abstract class AutoFillService extends Service { } /** - * Called when user requests service to auto-fill an {@link Activity}. + * Called by the Android system do decide if an {@link Activity} can be auto-filled by the + * service. + * + * <p>Service must call one of the {@link FillCallback} methods (like + * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)}) + * to notify the result of the request. * - * @param structure {@link Activity}'s view structure . - * @param data bundle with optional parameters (currently none) which is passed along on - * subsequent calls (so it can be used by the service to share data). + * @param structure {@link Activity}'s view structure. + * @param data bundle containing additional arguments set by the Android system (currently none) + * or data passed by the service on previous calls to fullfill other sections of this activity + * (see {@link FillResponse} Javadoc for examples of multiple-sections requests). * @param cancellationSignal signal for observing cancel requests. + * @param callback object used to notify the result of the request. */ public abstract void onFillRequest(AssistStructure structure, Bundle data, CancellationSignal cancellationSignal, FillCallback callback); @@ -168,29 +189,31 @@ public abstract class AutoFillService extends Service { /** * Called when user requests service to save the fields of an {@link Activity}. * + * <p>Service must call one of the {@link SaveCallback} methods (like + * {@link SaveCallback#onSuccess(AutoFillId[])} or {@link SaveCallback#onFailure(CharSequence)}) + * to notify the result of the request. + * * @param structure {@link Activity}'s view structure. - * @param data same bundle passed to - * {@link #onFillRequest(AssistStructure, Bundle, CancellationSignal, FillCallback)}; - * might also contain with optional parameters (currently none). + * @param data bundle containing additional arguments set by the Android system (currently none) + * or data passed by the service in the {@link FillResponse} that originated this call. * @param cancellationSignal signal for observing cancel requests. * @param callback object used to notify the result of the request. */ public abstract void onSaveRequest(AssistStructure structure, Bundle data, CancellationSignal cancellationSignal, SaveCallback callback); - private void requestAutoFill(AssistStructure structure, int flags, IBinder binder) { - // TODO(b/33197203): pass the Bundle received from mAssistReceiver instead? - final Bundle data = new Bundle(); + private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure, + Bundle data, int flags) { switch (flags) { - case ASSIST_FLAG_SANITIZED_TEXT: - final FillCallback fillCallback = new FillCallback(binder); + case AUTO_FILL_FLAG_TYPE_FILL: + final FillCallback fillCallback = new FillCallback(callback); // TODO(b/33197203): hook up the cancelationSignal onFillRequest(structure, data, new CancellationSignal(), fillCallback); break; - case ASSIST_FLAG_NON_SANITIZED_TEXT: - final SaveCallback saveCallback = new SaveCallback(binder); + case AUTO_FILL_FLAG_TYPE_SAVE: + final SaveCallback saveCallback = new SaveCallback(callback); // TODO(b/33197203): hook up the cancelationSignal - onSaveRequest(structure, null, new CancellationSignal(), saveCallback); + onSaveRequest(structure, data, new CancellationSignal(), saveCallback); break; default: Log.w(TAG, "invalid flag on requestAutoFill(): " + flags); @@ -198,7 +221,7 @@ public abstract class AutoFillService extends Service { } /** - * Called when the Android System disconnects from the service. + * Called when the Android system disconnects from the service. * * <p> At this point this service may no longer be an active {@link AutoFillService}. */ diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java index 3284b90304f5..5a9a9f68486c 100644 --- a/core/java/android/service/autofill/FillCallback.java +++ b/core/java/android/service/autofill/FillCallback.java @@ -18,19 +18,16 @@ package android.service.autofill; import static android.service.autofill.AutoFillService.DEBUG; +import android.annotation.Nullable; import android.app.Activity; -import android.app.assist.AssistStructure.ViewNode; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; -import android.util.SparseArray; +import android.view.autofill.FillResponse; import com.android.internal.util.Preconditions; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being * auto-filled. @@ -39,40 +36,50 @@ public final class FillCallback { private static final String TAG = "FillCallback"; - private final IAutoFillCallback mCallback; + private final IAutoFillServerCallback mCallback; + + private boolean mReplied = false; /** @hide */ - FillCallback(IBinder binder) { - mCallback = IAutoFillCallback.Stub.asInterface(binder); + FillCallback(IAutoFillServerCallback callback) { + mCallback = callback; } /** - * Auto-fills the {@link Activity}. + * Notifies the Android System that an + * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)} + * was successfully fulfilled by the service. * - * @throws RuntimeException if an error occurred while auto-filling it. + * @param response auto-fill information for that activity, or {@code null} when the activity + * cannot be auto-filled (for example, if it only contains read-only fields). + * + * @throws RuntimeException if an error occurred while calling the Android System. */ - public void onSuccess(FillData data) { - if (DEBUG) Log.d(TAG, "onSuccess(): data=" + data); + public void onSuccess(@Nullable FillResponse response) { + if (DEBUG) Log.d(TAG, "onSuccess(): respose=" + response); - Preconditions.checkArgument(data != null, "data cannot be null"); + checkNotRepliedYet(); try { - mCallback.autofill(data.asList()); + mCallback.showResponse(response); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** - * Notifies the {@link Activity} that the auto-fill request failed. + * Notifies the Android System that an + * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)} + * could not be fulfilled by the service. * - * @param message error message to be displayed. + * @param message error message to be displayed to the user. * - * @throws RuntimeException if an error occurred while notifying the activity. + * @throws RuntimeException if an error occurred while calling the Android System. */ public void onFailure(CharSequence message) { if (DEBUG) Log.d(TAG, "onFailure(): message=" + message); + checkNotRepliedYet(); Preconditions.checkArgument(message != null, "message cannot be null"); try { @@ -82,70 +89,9 @@ public final class FillCallback { } } - /** - * Data used to fill the fields of an {@link Activity}. - * - * <p>This class is immutable. - */ - public static final class FillData { - - private final List<FillableInputField> mList; - - private FillData(Builder builder) { - final int size = builder.mFields.size(); - final List<FillableInputField> list = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - list.add(builder.mFields.valueAt(i)); - } - mList = Collections.unmodifiableList(list); - // TODO: use FastImmutableArraySet or a similar structure instead? - } - - /** - * Gets the response as a {@code List} so it can be used in a binder call. - */ - List<FillableInputField> asList() { - return mList; - } - - @Override - public String toString() { - return "[AutoFillResponse: " + mList + "]"; - } - - /** - * Builder for {@link FillData} objects. - * - * <p>Typical usage: - * - * <pre class="prettyprint"> - * FillCallback.FillData data = new FillCallback.FillData.Builder() - * .setTextField(id1, "value 1") - * .setTextField(id2, "value 2") - * .build() - * </pre> - */ - public static class Builder { - private final SparseArray<FillableInputField> mFields = new SparseArray<>(); - - /** - * Auto-fills a text field. - * - * @param id view id as returned by {@link ViewNode#getAutoFillId()}. - * @param text text to be auto-filled. - * @return same builder so it can be chained. - */ - public Builder setTextField(int id, String text) { - mFields.put(id, FillableInputField.forText(id, text)); - return this; - } - - /** - * Builds a new {@link FillData} instance. - */ - public FillData build() { - return new FillData(this); - } - } + // There can be only one!! + private void checkNotRepliedYet() { + Preconditions.checkState(!mReplied, "already replied"); + mReplied = true; } } diff --git a/core/java/android/service/autofill/FillableInputField.java b/core/java/android/service/autofill/FillableInputField.java deleted file mode 100644 index 62950b40e5e1..000000000000 --- a/core/java/android/service/autofill/FillableInputField.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2016 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.service.autofill; - -import android.app.assist.AssistStructure.ViewNode; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Represents a view field that can be auto-filled. - * - * <p>Currently only text-fields are supported, so the value of the field can be obtained through - * {@link #getValue()}. - * - * @hide - */ -public final class FillableInputField implements Parcelable { - - private final int mId; - private final String mValue; - - private FillableInputField(int id, String value) { - mId = id; - mValue = value; - } - - private FillableInputField(Parcel parcel) { - mId = parcel.readInt(); - mValue = parcel.readString(); - } - - /** - * Gets the view id as returned by {@link ViewNode#getAutoFillId()}. - */ - public int getId() { - return mId; - } - - /** - * Gets the value of this field. - */ - public String getValue() { - return mValue; - - } - - @Override - public String toString() { - return "[AutoFillField: " + mId + "=" + mValue + "]"; - } - - /** - * Creates an {@code AutoFillField} for a text field. - * - * @param id view id as returned by {@link ViewNode#getAutoFillId()}. - * @param text value to be auto-filled. - */ - public static FillableInputField forText(int id, String text) { - return new FillableInputField(id, text); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeInt(mId); - parcel.writeString(mValue); - } - - public static final Parcelable.Creator<FillableInputField> CREATOR = - new Parcelable.Creator<FillableInputField>() { - @Override - public FillableInputField createFromParcel(Parcel source) { - return new FillableInputField(source); - } - - @Override - public FillableInputField[] newArray(int size) { - return new FillableInputField[size]; - } - }; -} diff --git a/core/java/android/service/autofill/IAutoFillCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl index d6d4f39651da..629b1f008d8f 100644 --- a/core/java/android/service/autofill/IAutoFillCallback.aidl +++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl @@ -18,10 +18,11 @@ package android.service.autofill; import java.util.List; +import android.view.autofill.Dataset; + /** * @hide */ -oneway interface IAutoFillCallback { - void autofill(in List values); - void showError(String message); +oneway interface IAutoFillAppCallback { + void autoFill(in Dataset dataset); } diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl index f1251c036469..f8ae57bb8809 100644 --- a/core/java/android/service/autofill/IAutoFillManagerService.aidl +++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl @@ -24,6 +24,5 @@ import android.os.Bundle; * {@hide} */ oneway interface IAutoFillManagerService { - - void requestAutoFill(IBinder activityToken, int userId, int flags); + void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags); } diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl new file mode 100644 index 000000000000..9d58c9956a25 --- /dev/null +++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 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.service.autofill; + +import java.util.List; + +import android.view.autofill.AutoFillId; +import android.view.autofill.FillResponse; + +/** + * @hide + */ +oneway interface IAutoFillServerCallback { + void showResponse(in FillResponse response); + void showError(String message); + void highlightSavedFields(in AutoFillId[] ids); +} diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl index bb122e5eff7d..a1f22bf114e3 100644 --- a/core/java/android/service/autofill/IAutoFillService.aidl +++ b/core/java/android/service/autofill/IAutoFillService.aidl @@ -18,14 +18,15 @@ package android.service.autofill; import android.app.assist.AssistStructure; import android.os.Bundle; -import android.service.autofill.IAutoFillCallback; +import android.service.autofill.IAutoFillServerCallback; import com.android.internal.os.IResultReceiver; /** * @hide */ -interface IAutoFillService { - oneway void onConnected(); - oneway void onDisconnected(); - IResultReceiver getAssistReceiver(); +oneway interface IAutoFillService { + void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback, + in Bundle extras, int flags); + void onConnected(); + void onDisconnected(); } diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index 4dc7392a963c..627d74c8e349 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -20,9 +20,11 @@ import static android.service.autofill.AutoFillService.DEBUG; import android.app.Activity; import android.app.assist.AssistStructure.ViewNode; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.autofill.AutoFillId; import com.android.internal.util.Preconditions; @@ -34,41 +36,53 @@ public final class SaveCallback { private static final String TAG = "SaveCallback"; - private final IAutoFillCallback mCallback; + private final IAutoFillServerCallback mCallback; + + private boolean mReplied = false; /** @hide */ - SaveCallback(IBinder binder) { - mCallback = IAutoFillCallback.Stub.asInterface(binder); + SaveCallback(IAutoFillServerCallback callback) { + mCallback = callback; } /** - * Notifies the {@link Activity} that the save request succeeded. + * Notifies the Android System that an + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)} + * was successfully fulfilled by the service. * * @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved. * - * @throws RuntimeException if an error occurred while saving the data. + * @throws RuntimeException if an error occurred while calling the Android System. */ - public void onSuccess(int[] ids) { + public void onSuccess(AutoFillId[] ids) { + if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ((ids == null) ? "null" : ids.length)); + Preconditions.checkArgument(ids != null, "ids cannot be null"); + checkNotRepliedYet(); Preconditions.checkArgument(ids.length > 0, "ids cannot be empty"); - if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ids.length); - - // TODO(b/33197203): display which ids were saved + try { + mCallback.highlightSavedFields(ids); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } } /** - * Notifies the {@link Activity} that the save request failed. + * Notifies the Android System that an + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)} + * could not be fulfilled by the service. * - * @param message error message to be displayed. + * @param message error message to be displayed to the user. * - * @throws RuntimeException if an error occurred while notifying the activity. + * @throws RuntimeException if an error occurred while calling the Android System. */ public void onFailure(CharSequence message) { if (DEBUG) Log.d(TAG, "onFailure(): message=" + message); Preconditions.checkArgument(message != null, "message cannot be null"); + checkNotRepliedYet(); try { mCallback.showError(message.toString()); @@ -76,4 +90,10 @@ public final class SaveCallback { e.rethrowAsRuntimeException(); } } + + // There can be only one!! + private void checkNotRepliedYet() { + Preconditions.checkState(!mReplied, "already replied"); + mReplied = true; + } } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 9ac89960ab95..1e9deebd8dcf 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -85,7 +85,7 @@ public class DynamicLayout extends Layout this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE, - ellipsize, ellipsizedWidth); + false /* justify */, ellipsize, ellipsizedWidth); } /** @@ -102,7 +102,8 @@ public class DynamicLayout extends Layout int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, boolean includepad, int breakStrategy, int hyphenationFrequency, - TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { + boolean justify, TextUtils.TruncateAt ellipsize, + int ellipsizedWidth) { super((ellipsize == null) ? display : (display instanceof Spanned) @@ -127,6 +128,7 @@ public class DynamicLayout extends Layout mIncludePad = includepad; mBreakStrategy = breakStrategy; + mJustify = justify; mHyphenationFrequency = hyphenationFrequency; /* @@ -300,7 +302,8 @@ public class DynamicLayout extends Layout .setEllipsizedWidth(mEllipsizedWidth) .setEllipsize(mEllipsizeAt) .setBreakStrategy(mBreakStrategy) - .setHyphenationFrequency(mHyphenationFrequency); + .setHyphenationFrequency(mHyphenationFrequency) + .setJustify(mJustify); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); // If the new layout has a blank line at the end, but it is not @@ -808,6 +811,7 @@ public class DynamicLayout extends Layout private TextUtils.TruncateAt mEllipsizeAt; private int mBreakStrategy; private int mHyphenationFrequency; + private boolean mJustify; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 3cb81b0a250f..2fc12d30ffd8 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -218,6 +218,11 @@ public abstract class Layout { mTextDir = textDir; } + /** @hide */ + protected void setJustify(boolean justify) { + mJustify = justify; + } + /** * Replace constructor properties of this Layout with new ones. Be careful. */ @@ -266,6 +271,99 @@ public abstract class Layout { drawText(canvas, firstLine, lastLine); } + private boolean isJustificationRequired(int lineNum) { + if (!mJustify) return false; + final int lineEnd = getLineEnd(lineNum); + return lineEnd < mText.length() && mText.charAt(lineEnd - 1) != '\n'; + } + + private float getJustifyWidth(int lineNum) { + Alignment paraAlign = mAlignment; + TabStops tabStops = null; + boolean tabStopsIsInitialized = false; + + int left = 0; + int right = mWidth; + + final int dir = getParagraphDirection(lineNum); + + ParagraphStyle[] spans = NO_PARA_SPANS; + if (mSpannedText) { + Spanned sp = (Spanned) mText; + final int start = getLineStart(lineNum); + + final boolean isFirstParaLine = (start == 0 || mText.charAt(start - 1) == '\n'); + + if (isFirstParaLine) { + final int spanEnd = sp.nextSpanTransition(start, mText.length(), + ParagraphStyle.class); + spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); + + for (int n = spans.length - 1; n >= 0; n--) { + if (spans[n] instanceof AlignmentSpan) { + paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); + break; + } + } + } + + final int length = spans.length; + boolean useFirstLineMargin = isFirstParaLine; + for (int n = 0; n < length; n++) { + if (spans[n] instanceof LeadingMarginSpan2) { + int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount(); + int startLine = getLineForOffset(sp.getSpanStart(spans[n])); + if (lineNum < startLine + count) { + useFirstLineMargin = true; + break; + } + } + } + for (int n = 0; n < length; n++) { + if (spans[n] instanceof LeadingMarginSpan) { + LeadingMarginSpan margin = (LeadingMarginSpan) spans[n]; + if (dir == DIR_RIGHT_TO_LEFT) { + right -= margin.getLeadingMargin(useFirstLineMargin); + } else { + left += margin.getLeadingMargin(useFirstLineMargin); + } + } + } + } + + if (getLineContainsTab(lineNum)) { + tabStops = new TabStops(TAB_INCREMENT, spans); + } + + final Alignment align; + if (paraAlign == Alignment.ALIGN_LEFT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; + } else if (paraAlign == Alignment.ALIGN_RIGHT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; + } else { + align = paraAlign; + } + + final int indentWidth; + if (align == Alignment.ALIGN_NORMAL) { + if (dir == DIR_LEFT_TO_RIGHT) { + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + } else { + indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + } + } else if (align == Alignment.ALIGN_OPPOSITE) { + if (dir == DIR_LEFT_TO_RIGHT) { + indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + } else { + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + } + } else { // Alignment.ALIGN_CENTER + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER); + } + + return right - left - indentWidth; + } + /** * @hide */ @@ -274,7 +372,7 @@ public abstract class Layout { int previousLineEnd = getLineStart(firstLine); ParagraphStyle[] spans = NO_PARA_SPANS; int spanEnd = 0; - TextPaint paint = mPaint; + final TextPaint paint = mPaint; CharSequence buf = mText; Alignment paraAlign = mAlignment; @@ -288,6 +386,7 @@ public abstract class Layout { for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) { int start = previousLineEnd; previousLineEnd = getLineStart(lineNum + 1); + final boolean justify = isJustificationRequired(lineNum); int end = getLineVisibleEnd(lineNum, start, previousLineEnd); int ltop = previousLineBottom; @@ -386,34 +485,42 @@ public abstract class Layout { } int x; + final int indentWidth; if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { - x = left + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + x = left + indentWidth; } else { - x = right + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + x = right - indentWidth; } } else { int max = (int)getLineExtent(lineNum, tabStops, false); if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { - x = right - max + getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); + x = right - max - indentWidth; } else { - x = left - max + getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); + x = left - max + indentWidth; } } else { // Alignment.ALIGN_CENTER + indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER); max = max & ~1; - x = ((right + left - max) >> 1) + - getIndentAdjust(lineNum, Alignment.ALIGN_CENTER); + x = ((right + left - max) >> 1) + indentWidth; } } paint.setHyphenEdit(getHyphen(lineNum)); Directions directions = getLineDirections(lineNum); - if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) { + if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab && !justify) { // XXX: assumes there's nothing additional to be done canvas.drawText(buf, start, end, x, lbaseline, paint); } else { tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops); + if (justify) { + tl.justify(right - left - indentWidth); + } tl.draw(canvas, x, ltop, lbaseline, lbottom); } paint.setHyphenEdit(0); @@ -1094,6 +1201,9 @@ public abstract class Layout { TextLine tl = TextLine.obtain(); mPaint.setHyphenEdit(getHyphen(line)); tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops); + if (isJustificationRequired(line)) { + tl.justify(getJustifyWidth(line)); + } float width = tl.metrics(null); mPaint.setHyphenEdit(0); TextLine.recycle(tl); @@ -1118,6 +1228,9 @@ public abstract class Layout { TextLine tl = TextLine.obtain(); mPaint.setHyphenEdit(getHyphen(line)); tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops); + if (isJustificationRequired(line)) { + tl.justify(getJustifyWidth(line)); + } float width = tl.metrics(null); mPaint.setHyphenEdit(0); TextLine.recycle(tl); @@ -1303,10 +1416,7 @@ public abstract class Layout { return end - 1; } - // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace() - if (!(ch == ' ' || ch == '\t' || ch == 0x1680 || - (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) || - ch == 0x205F || ch == 0x3000)) { + if (!TextLine.isLineEndSpace(ch)) { break; } @@ -2086,6 +2196,7 @@ public abstract class Layout { private boolean mSpannedText; private TextDirectionHeuristic mTextDir; private SpanSet<LineBackgroundSpan> mLineBackgroundSpans; + private boolean mJustify; public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 081be3afd6d3..cb5b073318d8 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -94,6 +94,7 @@ public class StaticLayout extends Layout { b.mMaxLines = Integer.MAX_VALUE; b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; + b.mJustify = false; b.mMeasuredText = MeasuredText.obtain(); return b; @@ -320,6 +321,17 @@ public class StaticLayout extends Layout { } /** + * Enables or disables paragraph justification. The default value is disabled (false). + * + * @param justify true for enabling and false for disabling paragraph justification. + * @return this builder, useful for chaining. + */ + public Builder setJustify(boolean justify) { + mJustify = justify; + return this; + } + + /** * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. * @@ -404,6 +416,7 @@ public class StaticLayout extends Layout { int mHyphenationFrequency; int[] mLeftIndents; int[] mRightIndents; + boolean mJustify; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -557,6 +570,7 @@ public class StaticLayout extends Layout { mLeftIndents = b.mLeftIndents; mRightIndents = b.mRightIndents; + setJustify(b.mJustify); generate(b, b.mIncludePad, b.mIncludePad); } @@ -676,7 +690,8 @@ public class StaticLayout extends Layout { nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency); + variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency, + b.mJustify); if (mLeftIndents != null || mRightIndents != null) { // TODO(raph) performance: it would be better to do this once per layout rather // than once per paragraph, but that would require a change to the native @@ -1284,7 +1299,8 @@ public class StaticLayout extends Layout { // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, float firstWidth, int firstWidthLineCount, float restWidth, - int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency); + int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency, + boolean isJustified); private static native float nAddStyleRun(long nativePtr, long nativePaint, long nativeTypeface, int start, int end, boolean isRtl); diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index c4118605b44c..fcff9a2e5fca 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -54,6 +54,10 @@ class TextLine { private char[] mChars; private boolean mCharsValid; private Spanned mSpanned; + + // Additional width of whitespace for justification. This value is per whitespace, thus + // the line width will increase by mAddedWidth x (number of stretchable whitespaces). + private float mAddedWidth; private final TextPaint mWorkPaint = new TextPaint(); private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); @@ -177,6 +181,25 @@ class TextLine { } } mTabs = tabStops; + mAddedWidth = 0; + } + + /** + * Justify the line to the given width. + */ + void justify(float justifyWidth) { + int end = mLen; + while (end > 0 && isLineEndSpace(mText.charAt(mStart + end - 1))) { + end--; + } + final int spaces = countStretchableSpaces(0, end); + if (spaces == 0) { + // There are no stretchable spaces, so we can't help the justification by adding any + // width. + return; + } + final float width = Math.abs(measure(end, false, null)); + mAddedWidth = (justifyWidth - width) / spaces; } /** @@ -595,6 +618,7 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); + wp.setWordSpacing(mAddedWidth); int spanStart = runStart; int spanLimit; @@ -695,6 +719,7 @@ class TextLine { Canvas c, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth, int offset) { + wp.setWordSpacing(mAddedWidth); // Get metrics first (even for empty strings or "0" width runs) if (fmi != null) { expandMetricsFromPaint(fmi, wp); @@ -981,5 +1006,34 @@ class TextLine { return TabStops.nextDefaultStop(h, TAB_INCREMENT); } + private boolean isStretchableWhitespace(int ch) { + // TODO: Support other stretchable whitespace. (Bug: 34013491) + return ch == 0x0020 || ch == 0x00A0; + } + + private int nextStretchableSpace(int start, int end) { + for (int i = start; i < end; i++) { + final char c = mCharsValid ? mChars[i] : mText.charAt(i + mStart); + if (isStretchableWhitespace(c)) return i; + } + return end; + } + + /* Return the number of spaces in the text line, for the purpose of justification */ + private int countStretchableSpaces(int start, int end) { + int count = 0; + for (int i = start; i < end; i = nextStretchableSpace(i + 1, end)) { + count++; + } + return count; + } + + // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace() + public static boolean isLineEndSpace(char ch) { + return ch == ' ' || ch == '\t' || ch == 0x1680 + || (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) + || ch == 0x205F || ch == 0x3000; + } + private static final int TAB_INCREMENT = 20; } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 58bc9a79d577..bb952ab93077 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -29,6 +29,8 @@ import android.os.Parcelable; import android.os.SystemProperties; import android.provider.Settings; import android.text.style.AbsoluteSizeSpan; +import android.text.style.AccessibilityClickableSpan; +import android.text.style.AccessibilityURLSpan; import android.text.style.AlignmentSpan; import android.text.style.BackgroundColorSpan; import android.text.style.BulletSpan; @@ -621,7 +623,11 @@ public class TextUtils { /** @hide */ public static final int TTS_SPAN = 24; /** @hide */ - public static final int LAST_SPAN = TTS_SPAN; + public static final int ACCESSIBILITY_CLICKABLE_SPAN = 25; + /** @hide */ + public static final int ACCESSIBILITY_URL_SPAN = 26; + /** @hide */ + public static final int LAST_SPAN = ACCESSIBILITY_URL_SPAN; /** * Flatten a CharSequence and whatever styles can be copied across processes @@ -803,6 +809,14 @@ public class TextUtils { readSpan(p, sp, new TtsSpan(p)); break; + case ACCESSIBILITY_CLICKABLE_SPAN: + readSpan(p, sp, new AccessibilityClickableSpan(p)); + break; + + case ACCESSIBILITY_URL_SPAN: + readSpan(p, sp, new AccessibilityURLSpan(p)); + break; + default: throw new RuntimeException("bogus span encoding " + kind); } diff --git a/core/java/android/text/style/AccessibilityClickableSpan.java b/core/java/android/text/style/AccessibilityClickableSpan.java new file mode 100644 index 000000000000..9c305ff0f6ae --- /dev/null +++ b/core/java/android/text/style/AccessibilityClickableSpan.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 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.text.style; + +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.ParcelableSpan; +import android.text.Spanned; +import android.text.TextUtils; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.R; + +import java.lang.ref.WeakReference; + + +/** + * {@link ClickableSpan} cannot be parceled, but accessibility services need to be able to cause + * their callback handlers to be called. This class serves as a parcelable placeholder for the + * real spans. + * + * This span is also passed back to an app's process when an accessibility service tries to click + * it. It contains enough information to track down the original clickable span so it can be + * called. + * + * @hide + */ +public class AccessibilityClickableSpan extends ClickableSpan + implements ParcelableSpan { + // The id of the span this one replaces + private final int mOriginalClickableSpanId; + + // Only retain a weak reference to the node to avoid referencing cycles that could create memory + // leaks. + private WeakReference<AccessibilityNodeInfo> mAccessibilityNodeInfoRef; + + + /** + * @param originalClickableSpanId The id of the span this one replaces + */ + public AccessibilityClickableSpan(int originalClickableSpanId) { + mOriginalClickableSpanId = originalClickableSpanId; + } + + public AccessibilityClickableSpan(Parcel p) { + mOriginalClickableSpanId = p.readInt(); + } + + @Override + public int getSpanTypeId() { + return getSpanTypeIdInternal(); + } + + @Override + public int getSpanTypeIdInternal() { + return TextUtils.ACCESSIBILITY_CLICKABLE_SPAN; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + writeToParcelInternal(dest, flags); + } + + @Override + public void writeToParcelInternal(Parcel dest, int flags) { + dest.writeInt(mOriginalClickableSpanId); + } + + /** + * Find the ClickableSpan that matches the one used to create this object. + * + * @param text The text that contains the original ClickableSpan. + * @return The ClickableSpan that matches this object, or {@code null} if no such object + * can be found. + */ + public ClickableSpan findClickableSpan(CharSequence text) { + if (!(text instanceof Spanned)) { + return null; + } + Spanned sp = (Spanned) text; + ClickableSpan[] os = sp.getSpans(0, text.length(), ClickableSpan.class); + for (int i = 0; i < os.length; i++) { + if (os[i].getId() == mOriginalClickableSpanId) { + return os[i]; + } + } + return null; + } + + /** + * Set the accessibilityNodeInfo that this placeholder belongs to. This node is not + * included in the parceling logic, and must be set to allow the onClick handler to function. + * + * @param accessibilityNodeInfo The info this span is part of + */ + public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) { + mAccessibilityNodeInfoRef = new WeakReference<>(accessibilityNodeInfo); + } + + /** + * Perform the click from an accessibility service. Will not work unless + * setAccessibilityNodeInfo is called with a properly initialized node. + * + * @param unused This argument is required by the superclass but is unused. The real view will + * be determined by the AccessibilityNodeInfo. + */ + @Override + public void onClick(View unused) { + if (mAccessibilityNodeInfoRef == null) { + return; + } + AccessibilityNodeInfo info = mAccessibilityNodeInfoRef.get(); + if (info == null) { + return; + } + Bundle arguments = new Bundle(); + arguments.putParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN, this); + + info.performAction(R.id.accessibilityActionClickOnClickableSpan, arguments); + } + + public static final Parcelable.Creator<AccessibilityClickableSpan> CREATOR = + new Parcelable.Creator<AccessibilityClickableSpan>() { + @Override + public AccessibilityClickableSpan createFromParcel(Parcel parcel) { + return new AccessibilityClickableSpan(parcel); + } + + @Override + public AccessibilityClickableSpan[] newArray(int size) { + return new AccessibilityClickableSpan[size]; + } + }; +} diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java new file mode 100644 index 000000000000..014700945c57 --- /dev/null +++ b/core/java/android/text/style/AccessibilityURLSpan.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 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.text.style; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; + +/** + * URLSpan's onClick method does not work from an accessibility service. This version of it does. + * It is used to replace URLSpans in {@link AccessibilityNodeInfo#setText(CharSequence)} + * @hide + */ +public class AccessibilityURLSpan extends URLSpan implements Parcelable { + final AccessibilityClickableSpan mAccessibilityClickableSpan; + + /** + * @param spanToReplace The original span + */ + public AccessibilityURLSpan(URLSpan spanToReplace) { + super(spanToReplace.getURL()); + mAccessibilityClickableSpan = + new AccessibilityClickableSpan(spanToReplace.getId()); + } + + public AccessibilityURLSpan(Parcel p) { + super(p); + mAccessibilityClickableSpan = new AccessibilityClickableSpan(p); + } + + @Override + public int getSpanTypeId() { + return getSpanTypeIdInternal(); + } + + @Override + public int getSpanTypeIdInternal() { + return TextUtils.ACCESSIBILITY_URL_SPAN; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + writeToParcelInternal(dest, flags); + } + + @Override + public void writeToParcelInternal(Parcel dest, int flags) { + super.writeToParcelInternal(dest, flags); + mAccessibilityClickableSpan.writeToParcel(dest, flags); + } + + @Override + public void onClick(View unused) { + mAccessibilityClickableSpan.onClick(unused); + } + + /** + * Delegated to AccessibilityClickableSpan + * @param accessibilityNodeInfo + */ + public void setAccessibilityNodeInfo(AccessibilityNodeInfo accessibilityNodeInfo) { + mAccessibilityClickableSpan.setAccessibilityNodeInfo(accessibilityNodeInfo); + } +} diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java index 989ef54f3f8a..a183427a44d0 100644 --- a/core/java/android/text/style/ClickableSpan.java +++ b/core/java/android/text/style/ClickableSpan.java @@ -26,12 +26,15 @@ import android.view.View; * be called. */ public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance { + private static int sIdCounter = 0; + + private int mId = sIdCounter++; /** * Performs the click action associated with this span. */ public abstract void onClick(View widget); - + /** * Makes the text underlined and in the link color. */ @@ -40,4 +43,14 @@ public abstract class ClickableSpan extends CharacterStyle implements UpdateAppe ds.setColor(ds.linkColor); ds.setUnderlineText(true); } + + /** + * Get the unique ID for this span. + * + * @return The unique ID. + * @hide + */ + public int getId() { + return mId; + } } diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 99f6c2a9c58d..79d16fbfaee8 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -59,7 +59,7 @@ public class EventLog { private Exception mLastWtf; // Layout of event log entry received from Android logger. - // see system/core/include/log/logger.h + // see system/core/include/log/log.h private static final int LENGTH_OFFSET = 0; private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index c7b1d03b120b..13ee48ef23ce 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN; + import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -24,8 +26,12 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; +import android.text.TextUtils; +import android.text.style.AccessibilityClickableSpan; +import android.text.style.ClickableSpan; import android.util.LongSparseArray; import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; @@ -33,6 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import com.android.internal.R; import com.android.internal.os.SomeArgs; import com.android.internal.util.Predicate; @@ -655,17 +662,23 @@ final class AccessibilityInteractionController { target = mViewRootImpl.mView; } if (target != null && isShown(target)) { - AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); - if (provider != null) { - if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - succeeded = provider.performAction(virtualDescendantId, action, - arguments); - } else { - succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, - action, arguments); + if (action == R.id.accessibilityActionClickOnClickableSpan) { + // Handle this hidden action separately + succeeded = handleClickableSpanActionUiThread( + target, virtualDescendantId, arguments); + } else { + AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); + if (provider != null) { + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + succeeded = provider.performAction(virtualDescendantId, action, + arguments); + } else { + succeeded = provider.performAction( + AccessibilityNodeProvider.HOST_VIEW_ID, action, arguments); + } + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + succeeded = target.performAccessibilityAction(action, arguments); } - } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - succeeded = target.performAccessibilityAction(action, arguments); } } } finally { @@ -816,6 +829,37 @@ final class AccessibilityInteractionController { return (appScale != 1.0f || (spec != null && !spec.isNop())); } + private boolean handleClickableSpanActionUiThread( + View view, int virtualDescendantId, Bundle arguments) { + Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN); + if (!(span instanceof AccessibilityClickableSpan)) { + return false; + } + + // Find the original ClickableSpan if it's still on the screen + AccessibilityNodeInfo infoWithSpan = null; + AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); + if (provider != null) { + int idForNode = (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) + ? AccessibilityNodeProvider.HOST_VIEW_ID : virtualDescendantId; + infoWithSpan = provider.createAccessibilityNodeInfo(idForNode); + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + infoWithSpan = view.createAccessibilityNodeInfo(); + } + if (infoWithSpan == null) { + return false; + } + + // Click on the corresponding span + ClickableSpan clickableSpan = ((AccessibilityClickableSpan) span).findClickableSpan( + infoWithSpan.getOriginalText()); + if (clickableSpan != null) { + clickableSpan.onClick(view); + return true; + } + return false; + } + /** * This class encapsulates a prefetching strategy for the accessibility APIs for * querying window content. It is responsible to prefetch a batch of diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 3beb00fc8d9e..b37ea8ebeaa0 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -282,6 +282,15 @@ public final class Display { */ public static final int STATE_DOZE_SUSPEND = 4; + /** + * Display state: The display is on and optimized for VR mode. + * + * @see #getState + * @see android.os.PowerManager#isInteractive + * @hide + */ + public static final int STATE_VR = 5; + /* The color mode constants defined below must be kept in sync with the ones in * system/graphics.h */ @@ -866,7 +875,8 @@ public final class Display { * Gets the state of the display, such as whether it is on or off. * * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON}, - * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, or {@link #STATE_UNKNOWN}. + * {@link #STATE_DOZE}, {@link #STATE_DOZE_SUSPEND}, or + * {@link #STATE_UNKNOWN}. */ public int getState() { synchronized (this) { @@ -982,6 +992,8 @@ public final class Display { return "DOZE"; case STATE_DOZE_SUSPEND: return "DOZE_SUSPEND"; + case STATE_VR: + return "VR"; default: return Integer.toString(state); } diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java index a3e8312c0a64..2fd2e966dc38 100644 --- a/core/java/android/view/SurfaceHolder.java +++ b/core/java/android/view/SurfaceHolder.java @@ -116,9 +116,34 @@ public interface SurfaceHolder { * size before it has been correctly drawn that way). This will * typically be preceeded by a call to {@link #surfaceChanged}. * + * As of O, {@link #surfaceRedrawNeededAsync} may be implemented + * to provide a non-blocking implementation. If {@link #surfaceRedrawNeededAsync} + * is not implemented, then this will be called instead. + * * @param holder The SurfaceHolder whose surface has changed. */ - public void surfaceRedrawNeeded(SurfaceHolder holder); + void surfaceRedrawNeeded(SurfaceHolder holder); + + /** + * An alternative to surfaceRedrawNeeded where it is not required to block + * until the redraw is complete. You should initiate the redraw, and return, + * later invoking drawingFinished when your redraw is complete. + * + * This can be useful to avoid blocking your main application thread on rendering. + * + * As of O, if this is implemented {@link #surfaceRedrawNeeded} will not be called. + * However it is still recommended to implement {@link #surfaceRedrawNeeded} for + * compatibility with older versions of the platform. + * + * @param holder The SurfaceHolder which needs redrawing. + * @param drawingFinished A runnable to signal completion. This may be invoked + * from any thread. + * + */ + default void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) { + surfaceRedrawNeeded(holder); + drawingFinished.run(); + } } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d46910c2139c..018be866cd8b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,6 +33,7 @@ import android.util.AttributeSet; import android.util.Log; import com.android.internal.view.BaseIWindow; +import com.android.internal.view.SurfaceCallbackHelper; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -639,21 +640,13 @@ public class SurfaceView extends View { if (callbacks == null) { callbacks = getSurfaceCallbacks(); } - for (SurfaceHolder.Callback c : callbacks) { - if (c instanceof SurfaceHolder.Callback2) { - ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( - mSurfaceHolder); - } - } + SurfaceCallbackHelper sch = + new SurfaceCallbackHelper(mSession, mWindow); + sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } } } finally { mIsCreating = false; - if (redrawNeeded) { - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "finishedDrawing"); - mSession.finishDrawing(mWindow); - } mSession.performDeferredDestroy(mWindow); } } catch (RemoteException ex) { @@ -876,7 +869,6 @@ public class SurfaceView extends View { } private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { - private static final String LOG_TAG = "SurfaceHolder"; @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0b1dfa2a321f..aedd0df4e4f0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -39,6 +39,7 @@ import android.annotation.Nullable; import android.annotation.Size; import android.annotation.TestApi; import android.annotation.UiThread; +import android.app.Application.OnProvideAssistDataListener; import android.content.ClipData; import android.content.Context; import android.content.ContextWrapper; @@ -103,6 +104,9 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Transformation; +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; +import android.view.autofill.VirtualViewDelegate; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; @@ -4025,18 +4029,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** - * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should not contain - * PII (Personally Identifiable Information). + * Set when a request was made to decide if views in an {@link android.app.Activity} can be + * auto-filled by an {@link android.service.autofill.AutoFillService}. + * + * <p>Since this request is made without a explicit user consent, the resulting + * {@link android.app.assist.AssistStructure} should not contain any PII + * (Personally Identifiable Information). + * + * <p>Examples: + * <ul> + * <li>{@link android.widget.TextView} texts should only be included when they were set by + * static resources. + * <li>{@link android.webkit.WebView} virtual children should be restricted to a subset of + * input fields and tags (like {@code id}). + * </ul> */ - // TODO(b/33197203) (b/33269702): improve documentation: mention all cases, show examples, etc. - public static final int ASSIST_FLAG_SANITIZED_TEXT = 0x1; + // TODO(b/33197203) (b/34078930): improve documentation: mention all cases, show examples, etc. + // In particular, be more specific about webview restrictions + public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x1; /** - * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should contain all - * type of data, even sensitive PII (Personally Identifiable Information) like passwords or - * credit card numbers. + * Set when the user explicitly asked a {@link android.service.autofill.AutoFillService} to save + * the value of the {@link View}s in an {@link android.app.Activity}. + * + * <p>The resulting {@link android.app.assist.AssistStructure} can contain any kind of PII + * (Personally Identifiable Information). For example, the text of password fields should be + * included since that's what's typically saved. */ - public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 0x2; + public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x2; /** * Set to true when drawing cache is enabled and cannot be created. @@ -6873,35 +6893,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. - * - * @deprecated As of API O sub-classes should override - * {@link #onProvideStructure(ViewStructure, int)} instead. */ - // TODO(b/33197203): set proper API above - @Deprecated public void onProvideStructure(ViewStructure structure) { - onProvideStructure(structure, 0); + onProvideStructureForAssistOrAutoFill(structure, 0); } /** - * Called when assist structure is being retrieved from a view as part of - * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part - * of an auto-fill request. - * - * <p>The default implementation fills in all data that can be inferred from the view itself. + * Called when assist structure is being retrieved from a view as part of an auto-fill request. * * <p>The structure must be filled according to the request type, which is set in the * {@code flags} parameter - see the documentation on each flag for more details. * - * @param structure Fill in with structured view data. The default implementation + * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. - * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and - * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info). + * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and + * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). */ - public void onProvideStructure(ViewStructure structure, int flags) { + public void onProvideAutoFillStructure(ViewStructure structure, int flags) { + onProvideStructureForAssistOrAutoFill(structure, flags); + } + + private void onProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. boolean forAutoFill = (flags - & (View.ASSIST_FLAG_SANITIZED_TEXT - | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0; + & (View.AUTO_FILL_FLAG_TYPE_FILL + | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; final int id = mID; if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0 && (id&0x0000ffff) != 0) { @@ -6923,6 +6940,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to // reuse the accessibility id to save space. structure.setAutoFillId(getAccessibilityViewId()); + + structure.setAutoFillType(getAutoFillType()); } structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop); @@ -6973,40 +6992,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. - * - * @deprecated As of API O, sub-classes should override - * {@link #onProvideVirtualStructure(ViewStructure, int)} instead. */ - // TODO(b/33197203): set proper API above - @Deprecated public void onProvideVirtualStructure(ViewStructure structure) { - onProvideVirtualStructure(structure, 0); + onProvideVirtualStructureForAssistOrAutoFill(structure, 0); } /** - * Called when assist structure is being retrieved from a view as part of - * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part - * of an auto-fill request to generate additional virtual structure under this view. + * Called when assist structure is being retrieved from a view as part of an auto-fill request + * to generate additional virtual structure under this view. * * <p>The defaullt implementation uses {@link #getAccessibilityNodeProvider()} to try to - * generate this from the view's virtual accessibility nodes, if any. You can override this + * generate this from the view's virtual accessibility nodes, if any. You can override this * for a more optimal implementation providing this data. * * <p>The structure must be filled according to the request type, which is set in the * {@code flags} parameter - see the documentation on each flag for more details. * * @param structure Fill in with structured view data. - * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and - * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info). + * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and + * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). */ - public void onProvideVirtualStructure(ViewStructure structure, int flags) { - boolean sanitize = (flags & View.ASSIST_FLAG_SANITIZED_TEXT) != 0; - - if (sanitize) { - // TODO(b/33197203): change populateVirtualStructure so it sanitizes data in this case. - return; - } + public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { + onProvideVirtualStructureForAssistOrAutoFill(structure, flags); + } + private void onProvideVirtualStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { AccessibilityNodeInfo info = createAccessibilityNodeInfo(); @@ -7017,8 +7029,66 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + /** + * Gets the {@link VirtualViewDelegate} responsible for auto-filling the virtual children of + * this view. + * + * <p>By default returns {@code null} but should be overridden when view provides a virtual + * hierachy on {@link OnProvideAssistDataListener} that takes flags used by the AutoFill + * Framework (such as {@link #AUTO_FILL_FLAG_TYPE_FILL} and + * {@link #AUTO_FILL_FLAG_TYPE_SAVE}). + */ + @Nullable + public VirtualViewDelegate getAutoFillVirtualViewDelegate( + @SuppressWarnings("unused") VirtualViewDelegate.Callback callback) { + return null; + } + + /** + * Automatically fills the content of this view with the {@code value}. + * + * <p>By default does nothing, but views should override it (and {@link #getAutoFillType()} to + * support the AutoFill Framework. + * + * <p>Typically, it is implemented by: + * + * <ol> + * <li>Call the proper getter method on {@link AutoFillValue} to fetch the actual value. + * <li>Pass the actual value to the equivalent setter in the view. + * <ol> + * + * <p>For example, a text-field view would call: + * + * <pre class="prettyprint"> + * CharSequence text = value.getTextValue(); + * if (text != null) { + * setText(text); + * } + * </pre> + */ + public void autoFill(@SuppressWarnings("unused") AutoFillValue value) { + } + + /** + * Describes the auto-fill type that should be used on callas to + * {@link #autoFill(AutoFillValue)} and + * {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}. + * + * <p>By default returns {@code null}, but views should override it (and + * {@link #autoFill(AutoFillValue)} to support the AutoFill Framework. + */ + @Nullable + public AutoFillType getAutoFillType() { + return null; + } + private void populateVirtualStructure(ViewStructure structure, AccessibilityNodeProvider provider, AccessibilityNodeInfo info, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. + + final boolean sanitized = (flags & View.AUTO_FILL_FLAG_TYPE_FILL) != 0; + structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), null, null, null); Rect rect = structure.getTempRect(); @@ -7056,7 +7126,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, CharSequence cname = info.getClassName(); structure.setClassName(cname != null ? cname.toString() : null); structure.setContentDescription(info.getContentDescription()); - if (info.getText() != null || info.getError() != null) { + if (!sanitized && (info.getText() != null || info.getError() != null)) { + // TODO(b/33197203) (b/33269702): when sanitized, try to use the Accessibility API to + // just set sanitized values (like text coming from resource files), rather than not + // setting it at all. structure.setText(info.getText(), info.getTextSelectionStart(), info.getTextSelectionEnd()); } @@ -7077,14 +7150,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Dispatch creation of {@link ViewStructure} down the hierarchy. The default * implementation calls {@link #onProvideStructure} and * {@link #onProvideVirtualStructure}. - * - * @deprecated As of API O, sub-classes should override - * {@link #dispatchProvideStructure(ViewStructure, int)} instead. */ - // TODO(b/33197203): set proper API above - @Deprecated public void dispatchProvideStructure(ViewStructure structure) { - dispatchProvideStructure(structure, 0); + dispatchProvideStructureForAssistOrAutoFill(structure, 0); } /** @@ -7093,22 +7161,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>The structure must be filled according to the request type, which is set in the * {@code flags} parameter - see the documentation on each flag for more details. * - * <p>The default implementation calls {@link #onProvideStructure(ViewStructure, int)} and - * {@link #onProvideVirtualStructure(ViewStructure, int)}. + * <p>The default implementation calls {@link #onProvideAutoFillStructure(ViewStructure, int)} + * and {@link #onProvideAutoFillVirtualStructure(ViewStructure, int)}. * * @param structure Fill in with structured view data. - * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and - * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info). + * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and + * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). */ - public void dispatchProvideStructure(ViewStructure structure, int flags) { + public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) { + dispatchProvideStructureForAssistOrAutoFill(structure, flags); + } + + private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. boolean forAutoFill = (flags - & (View.ASSIST_FLAG_SANITIZED_TEXT - | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0; + & (View.AUTO_FILL_FLAG_TYPE_FILL + | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked(); if (!blocked) { - onProvideStructure(structure, flags); - onProvideVirtualStructure(structure, flags); + if (forAutoFill) { + onProvideAutoFillStructure(structure, flags); + onProvideAutoFillVirtualStructure(structure, flags); + } else { + onProvideStructure(structure); + onProvideVirtualStructure(structure); + } } else { structure.setClassName(getAccessibilityClassName().toString()); structure.setAssistBlocked(true); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 56501ecd9f4a..1f1af4bf0094 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3156,18 +3156,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation + * adds in all child views of the view group, in addition to calling the default View + * implementation. + */ + @Override + public void dispatchProvideStructure(ViewStructure structure) { + super.dispatchProvideStructure(structure); + dispatchProvideStructureForAssistOrAutoFill(structure, 0); + } + + /** * {@inheritDoc} * * <p>This implementation adds in all child views of the view group, in addition to calling the * default {@link View} implementation. */ @Override - public void dispatchProvideStructure(ViewStructure structure, int flags) { - super.dispatchProvideStructure(structure, flags); + public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) { + super.dispatchProvideAutoFillStructure(structure, flags); + dispatchProvideStructureForAssistOrAutoFill(structure, flags); + } + private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. boolean forAutoFill = (flags - & (View.ASSIST_FLAG_SANITIZED_TEXT - | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0; + & (View.AUTO_FILL_FLAG_TYPE_FILL + | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked(); @@ -3233,12 +3249,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager preorderedList, children, childIndex); final ViewStructure cstructure = structure.newChild(i); - // Must explicitly check which recursive method to call because child might - // not be overriding the new, flags-based version - if (flags == 0) { - child.dispatchProvideStructure(cstructure); + // Must explicitly check which recursive method to call. + if (forAutoFill) { + child.dispatchProvideAutoFillStructure(cstructure, flags); } else { - child.dispatchProvideStructure(cstructure, flags); + child.dispatchProvideStructure(cstructure); } } if (preorderedList != null) preorderedList.clear(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d6db634e6c64..b42f7695c3de 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -96,6 +96,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; +import com.android.internal.view.SurfaceCallbackHelper; import java.io.FileDescriptor; import java.io.IOException; @@ -528,11 +529,17 @@ public final class ViewRootImpl implements ViewParent, */ public void notifyChildRebuilt() { if (mView instanceof RootViewSurfaceTaker) { + if (mSurfaceHolderCallback != null) { + mSurfaceHolder.removeCallback(mSurfaceHolderCallback); + } + mSurfaceHolderCallback = ((RootViewSurfaceTaker)mView).willYouTakeTheSurface(); + if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); + mSurfaceHolder.addCallback(mSurfaceHolderCallback); } else { mSurfaceHolder = null; } @@ -581,6 +588,7 @@ public final class ViewRootImpl implements ViewParent, if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); + mSurfaceHolder.addCallback(mSurfaceHolderCallback); } } @@ -1957,7 +1965,6 @@ public final class ViewRootImpl implements ViewParent, mSurfaceHolder.ungetCallbacks(); mIsCreating = true; - mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { @@ -1967,8 +1974,6 @@ public final class ViewRootImpl implements ViewParent, surfaceChanged = true; } if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) { - mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder, - lp.format, mWidth, mHeight); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { @@ -1981,7 +1986,6 @@ public final class ViewRootImpl implements ViewParent, } else if (hadSurface) { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); @@ -2646,21 +2650,18 @@ public final class ViewRootImpl implements ViewParent, if (LOCAL_LOGV) { Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle()); } + if (mSurfaceHolder != null && mSurface.isValid()) { - mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - if (callbacks != null) { - for (SurfaceHolder.Callback c : callbacks) { - if (c instanceof SurfaceHolder.Callback2) { - ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder); - } - } + + sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); + } else { + try { + mWindowSession.finishDrawing(mWindow); + } catch (RemoteException e) { } } - try { - mWindowSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } } } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index e9ff9d0edba6..839e11c01303 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -19,6 +19,10 @@ package android.view; import android.graphics.Matrix; import android.graphics.Rect; import android.os.Bundle; +import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; +import android.view.autofill.VirtualViewDelegate; /** * Container for storing additional per-view data generated by {@link View#onProvideStructure @@ -258,6 +262,12 @@ public abstract class ViewStructure { public abstract ViewStructure newChild(int index); /** + * Like {@link #newChild(int)}, but providing a {@code virtualId} to the child so it can be + * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}. + */ + public abstract ViewStructure newChild(int index, int virtualId); + + /** * Like {@link #newChild}, but allows the caller to asynchronously populate the returned * child. It can transfer the returned {@link ViewStructure} to another thread for it * to build its content (and children etc). Once done, some thread must call @@ -268,6 +278,17 @@ public abstract class ViewStructure { public abstract ViewStructure asyncNewChild(int index); /** + * Like {@link #asyncNewChild(int)}, but providing a {@code virtualId} to the child so it can be + * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}. + */ + public abstract ViewStructure asyncNewChild(int index, int virtualId); + + /** + * Sets the {@link AutoFillType} that can be used to auto-fill this node. + */ + public abstract void setAutoFillType(AutoFillType info); + + /** * Call when done populating a {@link ViewStructure} returned by * {@link #asyncNewChild}. */ @@ -277,5 +298,8 @@ public abstract class ViewStructure { public abstract Rect getTempRect(); /** @hide */ - public abstract void setAutoFillId(int autoFillId); + public abstract void setAutoFillId(int viewId); + + /** @hide */ + public abstract AutoFillId getAutoFillId(); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index caa09cccbab1..dd852567360f 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -437,6 +437,15 @@ public interface WindowManagerPolicy { } /** + * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the + * contents of the starting window into an class implementing this interface, which then will be + * held by WM and passed into {@link #removeSplashScreen} when the starting window is no + * longer needed. + */ + interface StartingSurface { + } + + /** * Interface for calling back in to the window manager that is private * between it and the policy. */ @@ -738,17 +747,17 @@ public interface WindowManagerPolicy { * context to for resources. * * @return Optionally you can return the View that was used to create the - * window, for easy removal in removeStartingWindow. + * window, for easy removal in removeSplashScreen. * - * @see #removeStartingWindow + * @see #removeSplashScreen */ - public View addStartingWindow(IBinder appToken, String packageName, - int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, - int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig); + public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, + CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, + int logo, int windowFlags, Configuration overrideConfig); /** * Called when the first window of an application has been displayed, while - * {@link #addStartingWindow} has created a temporary initial window for + * {@link #addSplashScreen} has created a temporary initial window for * that application. You should at this point remove the window from the * window manager. This is called without the window manager locked so * that you can call back into it. @@ -759,11 +768,11 @@ public interface WindowManagerPolicy { * even if you previously returned one. * * @param appToken Token of the application that has started. - * @param window Window View that was returned by createStartingWindow. + * @param surface Surface that was returned by {@link #addSplashScreen}. * - * @see #addStartingWindow + * @see #addSplashScreen */ - public void removeStartingWindow(IBinder appToken, View window); + public void removeSplashScreen(IBinder appToken, StartingSurface surface); /** * Prepare for a window being added to the window manager. You can throw an diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 91278612072b..91468dae703e 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -25,7 +25,13 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; +import android.text.Spannable; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.AccessibilityClickableSpan; +import android.text.style.AccessibilityURLSpan; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; import android.util.ArraySet; import android.util.LongArray; import android.util.Pools.SynchronizedPool; @@ -464,6 +470,14 @@ public class AccessibilityNodeInfo implements Parcelable { public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE"; + /** + * Argument to pass the {@link AccessibilityClickableSpan}. + * For use with R.id.accessibilityActionClickOnClickableSpan + * @hide + */ + public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN = + "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN"; + // Focus types /** @@ -628,6 +642,8 @@ public class AccessibilityNodeInfo implements Parcelable { private CharSequence mPackageName; private CharSequence mClassName; + // Hidden, unparceled value used to hold the original value passed to setText + private CharSequence mOriginalText; private CharSequence mText; private CharSequence mError; private CharSequence mContentDescription; @@ -2213,14 +2229,49 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Gets the text of this node. + * <p> + * <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s, + * these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)} + * can be called from an {@link AccessibilityService}. When called from a service, the + * {@link View} argument is ignored and the corresponding span will be found on the view that + * this {@code AccessibilityNodeInfo} represents and called with that view as its argument. + * <p> + * This treatment of {@link ClickableSpan}s means that the text returned from this method may + * different slightly one passed to {@link #setText(CharSequence)}, although they will be + * equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The + * {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside + * of an accessibility service. + * </p> * * @return The text. */ public CharSequence getText() { + // Attach this node to any spans that need it + if (mText instanceof Spanned) { + Spanned spanned = (Spanned) mText; + AccessibilityClickableSpan[] clickableSpans = + spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class); + for (int i = 0; i < clickableSpans.length; i++) { + clickableSpans[i].setAccessibilityNodeInfo(this); + } + AccessibilityURLSpan[] urlSpans = + spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class); + for (int i = 0; i < urlSpans.length; i++) { + urlSpans[i].setAccessibilityNodeInfo(this); + } + } return mText; } /** + * Get the text passed to setText before any changes to the spans. + * @hide + */ + public CharSequence getOriginalText() { + return mOriginalText; + } + + /** * Sets the text of this node. * <p> * <strong>Note:</strong> Cannot be called from an @@ -2234,6 +2285,34 @@ public class AccessibilityNodeInfo implements Parcelable { */ public void setText(CharSequence text) { enforceNotSealed(); + mOriginalText = text; + // Replace any ClickableSpans in mText with placeholders + if (text instanceof Spanned) { + ClickableSpan[] spans = + ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); + if (spans.length > 0) { + Spannable spannable = Spannable.Factory.getInstance().newSpannable(text); + for (int i = 0; i < spans.length; i++) { + ClickableSpan span = spans[i]; + if ((span instanceof AccessibilityClickableSpan) + || (span instanceof AccessibilityURLSpan)) { + // We've already done enough + break; + } + int spanToReplaceStart = spannable.getSpanStart(span); + int spanToReplaceEnd = spannable.getSpanEnd(span); + int spanToReplaceFlags = spannable.getSpanFlags(span); + spannable.removeSpan(span); + ClickableSpan replacementSpan = (span instanceof URLSpan) + ? new AccessibilityURLSpan((URLSpan) span) + : new AccessibilityClickableSpan(span.getId()); + spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd, + spanToReplaceFlags); + } + mText = spannable; + return; + } + } mText = (text == null) ? null : text.subSequence(0, text.length()); } diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 09d4dfc93b19..767024ecfecf 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -237,7 +237,7 @@ public class AnimationSet extends Animation { mDuration = a.getStartOffset() + a.getDuration(); mLastEnd = mStartOffset + mDuration; } else { - mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration()); + mLastEnd = Math.max(mLastEnd, mStartOffset + a.getStartOffset() + a.getDuration()); mDuration = mLastEnd - mStartOffset; } } diff --git a/core/java/android/view/autofill/AutoFillId.aidl b/core/java/android/view/autofill/AutoFillId.aidl new file mode 100644 index 000000000000..56f0338ccafb --- /dev/null +++ b/core/java/android/view/autofill/AutoFillId.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.view.autofill; + +parcelable AutoFillId;
\ No newline at end of file diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java new file mode 100644 index 000000000000..b7b694d26030 --- /dev/null +++ b/core/java/android/view/autofill/AutoFillId.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A unique identifier for an auto-fill node inside an {@link android.app.Activity}. + */ +public final class AutoFillId implements Parcelable { + + private int mViewId; + private boolean mVirtual; + private int mVirtualId; + + /** @hide */ + public AutoFillId(int id) { + mVirtual = false; + mViewId = id; + } + + /** @hide */ + public AutoFillId(AutoFillId parent, int virtualChildId) { + mVirtual = true; + mViewId = parent.mViewId; + mVirtualId = virtualChildId; + } + + /** @hide */ + public int getViewId() { + return mViewId; + } + + /** @hide */ + public int getVirtualChildId() { + return mVirtualId; + } + + /** @hide */ + public boolean isVirtual() { + return mVirtual; + } + + ///////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////// + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mViewId; + result = prime * result + mVirtualId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final AutoFillId other = (AutoFillId) obj; + if (mViewId != other.mViewId) return false; + if (mVirtualId != other.mVirtualId) return false; + return true; + } + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + final StringBuilder builder = new StringBuilder("FieldId [viewId=").append(mViewId); + if (mVirtual) { + builder.append(", virtualId=").append(mVirtualId); + } + return builder.append(']').toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mViewId); + parcel.writeInt(mVirtual ? 1 : 0); + parcel.writeInt(mVirtualId); + } + + private AutoFillId(Parcel parcel) { + mViewId = parcel.readInt(); + mVirtual = parcel.readInt() == 1; + mVirtualId = parcel.readInt(); + } + + public static final Parcelable.Creator<AutoFillId> CREATOR = + new Parcelable.Creator<AutoFillId>() { + @Override + public AutoFillId createFromParcel(Parcel source) { + return new AutoFillId(source); + } + + @Override + public AutoFillId[] newArray(int size) { + return new AutoFillId[size]; + } + }; +} diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/view/autofill/AutoFillType.aidl new file mode 100644 index 000000000000..a63d7c50f38b --- /dev/null +++ b/core/java/android/view/autofill/AutoFillType.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016, 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.view.autofill; + +parcelable AutoFillType;
\ No newline at end of file diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java new file mode 100644 index 000000000000..017f7f8ca3e8 --- /dev/null +++ b/core/java/android/view/autofill/AutoFillType.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; +import android.widget.TextView; + +/** + * Defines the type of a object that can be used to auto-fill a {@link View} so the + * {@link android.service.autofill.AutoFillService} can use the proper {@link AutoFillValue} to + * fill it. + * + * <p>Some {@link AutoFillType}s can have an optional {@code sub-type}: the + * main {@code type} defines the view's UI control category (like a text field), while the optional + * {@code sub-type} define its semantics (like a postal address). + */ +public final class AutoFillType implements Parcelable { + + // Cached instance for types that don't have subtype; it uses the "lazy initialization holder + // class idiom" (Effective Java, Item 71) to avoid memory utilization when auto-fill is not + // enabled. + private static class DefaultTypesHolder { + static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE, 0); + static final AutoFillType LIST = new AutoFillType(TYPE_LIST, 0); + } + + private static final int TYPE_TEXT = 1; + private static final int TYPE_TOGGLE = 2; + // TODO(b/33197203): make sure it works with Spinners and/or add a new type for them + // (since they're often used for credit card selection) + private static final int TYPE_LIST = 3; + + // TODO(b/33197203): add others, like date picker? That would be trick, because they're set as: + // updateDate(int year, int month, int dayOfMonth) + // So, we would have to either use a long representing the Date.time(), or a custom long + // representing: + // year * 10000 + month * 100 + day + // Then a custom getDatePickerValue(Bundle) that returns an immutable object with these 3 fields + + private final int mType; + private final int mSubType; + + private AutoFillType(int type, int subType) { + mType = type; + mSubType = subType; + } + + /** + * Checks if this is a type for a text field, which is filled by a {@link CharSequence}. + * + * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through + * {@link AutoFillValue#forText(CharSequence)}, and the value of a bundle passed to auto-fill a + * {@link View} can be fetched through {@link AutoFillValue#getTextValue()}. + * + * <p>Sub-type for this type is the value defined by {@link TextView#getInputType()}. + */ + public boolean isText() { + return mType == TYPE_TEXT; + } + + /** + * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}. + * + * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through + * {@link AutoFillValue#forToggle(boolean)}, and the value of a bundle passed to auto-fill a + * {@link View} can be fetched through {@link AutoFillValue#getToggleValue()}. + * + * <p>This type has no sub-types. + */ + public boolean isToggle() { + return mType == TYPE_TOGGLE; + } + + /** + * Checks if this is a type for a selection list field, which is filled by a {@code integer} + * representing the element index inside the list (starting at {@code 0}. + * + * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through + * {@link AutoFillValue#forList(int)}, and the value of a bundle passed to auto-fill a + * {@link View} can be fetched through {@link AutoFillValue#getListValue()}. + * + * <p>This type has no sub-types. + */ + public boolean isList() { + return mType == TYPE_LIST; + } + + + /** + * Gets the optional sub-type, representing the {@link View}'s semantic. + * + * @return {@code 0} if type does not support sub-types. + */ + public int getSubType() { + return mSubType; + } + + ///////////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////////// + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + return "AutoFillType [type=" + mType + ", subType=" + mSubType + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mSubType; + result = prime * result + mType; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final AutoFillType other = (AutoFillType) obj; + if (mSubType != other.mSubType) return false; + if (mType != other.mType) return false; + return true; + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mType); + parcel.writeInt(mSubType); + } + + private AutoFillType(Parcel parcel) { + mType = parcel.readInt(); + mSubType = parcel.readInt(); + } + + public static final Parcelable.Creator<AutoFillType> CREATOR = + new Parcelable.Creator<AutoFillType>() { + @Override + public AutoFillType createFromParcel(Parcel source) { + return new AutoFillType(source); + } + + @Override + public AutoFillType[] newArray(int size) { + return new AutoFillType[size]; + } + }; + + //////////////////// + // Factory methods // + //////////////////// + + /** + * Creates a text field type, which is filled by a {@link CharSequence}. + * + * <p>See {@link #isText()} for more info. + */ + public static AutoFillType forText(int inputType) { + return new AutoFillType(TYPE_TEXT, inputType); + } + + /** + * Creates a type that can be toggled which is filled by a {@code boolean}. + * + * <p>See {@link #isToggle()} for more info. + */ + public static AutoFillType forToggle() { + return DefaultTypesHolder.TOGGLE; + } + + /** + * Creates a selection list, which is filled by a {@code integer} representing the element index + * inside the list (starting at {@code 0}. + * + * <p>See {@link #isList()} for more info. + */ + public static AutoFillType forList() { + return DefaultTypesHolder.LIST; + } +} diff --git a/core/java/android/view/autofill/AutoFillValue.aidl b/core/java/android/view/autofill/AutoFillValue.aidl new file mode 100644 index 000000000000..3b284b96dc7c --- /dev/null +++ b/core/java/android/view/autofill/AutoFillValue.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016, 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.view.autofill; + +parcelable AutoFillValue;
\ No newline at end of file diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java new file mode 100644 index 000000000000..c39f26b9b864 --- /dev/null +++ b/core/java/android/view/autofill/AutoFillValue.java @@ -0,0 +1,150 @@ +/* + * 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; + +/** + * Abstracts how a {@link View} can be auto-filled by an + * {@link android.service.autofill.AutoFillService}. + * + * <p>Each {@link AutoFillValue} has a {@code type} and optionally a {@code sub-type}: the + * {@code type} defines the view's UI control category (like a text field), while the optional + * {@code sub-type} define its semantics (like a postal address). + */ +public final class AutoFillValue implements Parcelable { + + private final CharSequence mText; + private final int mListIndex; + private final boolean mToggle; + + private AutoFillValue(CharSequence text, int listIndex, boolean toggle) { + mText = text; + mListIndex = listIndex; + mToggle = toggle; + } + + /** + * Gets the value to auto-fill a text field. + * + * <p>See {@link AutoFillType#isText()} for more info. + */ + public CharSequence getTextValue() { + return mText; + } + + /** + * Gets the value to auto-fill a toggable field. + * + * <p>See {@link AutoFillType#isToggle()} for more info. + */ + public boolean getToggleValue() { + return mToggle; + } + + /** + * Gets the value to auto-fill a selection list field. + * + * <p>See {@link AutoFillType#isList()} for more info. + */ + public int getListValue() { + return mListIndex; + } + + ///////////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////////// + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + return "AutoFillValue[text=" + mText + ", listIndex=" + mListIndex + ", toggle=" + mToggle + + "]"; + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeCharSequence(mText); + parcel.writeInt(mListIndex); + parcel.writeInt(mToggle ? 1 : 0); + } + + private AutoFillValue(Parcel parcel) { + mText = parcel.readCharSequence(); + mListIndex = parcel.readInt(); + mToggle = parcel.readInt() == 1; + } + + public static final Parcelable.Creator<AutoFillValue> CREATOR = + new Parcelable.Creator<AutoFillValue>() { + @Override + public AutoFillValue createFromParcel(Parcel source) { + return new AutoFillValue(source); + } + + @Override + public AutoFillValue[] newArray(int size) { + return new AutoFillValue[size]; + } + }; + + //////////////////// + // Factory methods // + //////////////////// + + // TODO(b/33197203): add unit tests for each supported type (new / get should return same value) + /** + * Creates a new {@link AutoFillValue} to auto-fill a text field. + * + * <p>See {@link AutoFillType#isText()} for more info. + */ + public static AutoFillValue forText(CharSequence value) { + return new AutoFillValue(value, 0, false); + } + + /** + * Creates a new {@link AutoFillValue} to auto-fill a toggable field. + * + * <p>See {@link AutoFillType#isToggle()} for more info. + */ + public static AutoFillValue forToggle(boolean value) { + return new AutoFillValue(null, 0, value); + } + + /** + * Creates a new {@link AutoFillValue} to auto-fill a selection list field. + * + * <p>See {@link AutoFillType#isList()} for more info. + */ + public static AutoFillValue forList(int value) { + return new AutoFillValue(null, value, false); + } +} diff --git a/core/java/android/view/autofill/Dataset.aidl b/core/java/android/view/autofill/Dataset.aidl new file mode 100644 index 000000000000..2a8e67caace1 --- /dev/null +++ b/core/java/android/view/autofill/Dataset.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016, 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.view.autofill; + +parcelable Dataset;
\ No newline at end of file diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java new file mode 100644 index 000000000000..a73eb7740e80 --- /dev/null +++ b/core/java/android/view/autofill/Dataset.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; +import static android.view.autofill.Helper.append; + +import android.app.Activity; +import android.app.assist.AssistStructure.ViewNode; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.autofill.AutoFillService; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A set of data that can be used to auto-fill an {@link Activity}. + * + * <p>It contains: + * + * <ol> + * <li>A name used to identify the dataset in the UI. + * <li>A list of id/value pairs for the fields that can be auto-filled. + * <li>An optional {@link Bundle} with extras (used only by the service creating it). + * </ol> + * + * See {@link FillResponse} for examples. + */ +public final class Dataset implements Parcelable { + + private final CharSequence mName; + private final ArrayList<DatasetField> mFields; + private final Bundle mExtras; + + private Dataset(Dataset.Builder builder) { + mName = builder.mName; + // TODO(b/33197203): make an immutable copy of mFields? + mFields = builder.mFields; + mExtras = builder.mExtras; + } + + /** @hide */ + public CharSequence getName() { + return mName; + } + + /** @hide */ + public List<DatasetField> getFields() { + return mFields; + } + + /** @hide */ + public Bundle getExtras() { + return mExtras; + } + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName) + .append(", fields=").append(mFields).append(", extras="); + append(builder, mExtras); + return builder.append(']').toString(); + } + + /** + * A builder for {@link Dataset} objects. + */ + public static final class Builder { + private CharSequence mName; + private final ArrayList<DatasetField> mFields = new ArrayList<>(); + private Bundle mExtras; + + /** + * Creates a new builder. + * + * @param name Name used to identify the dataset in the UI. Typically it's the same value as + * the first field in the dataset (like username or email address) or an user-provided name + * (like "My Work Address"). + */ + public Builder(CharSequence name) { + mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null"); + } + + /** + * Sets the value of a field. + * + * @param id id returned by {@link ViewNode#getAutoFillId()}. + * @param value value to be auto filled. + */ + public Dataset.Builder setValue(AutoFillId id, AutoFillValue value) { + putField(new DatasetField(id, value)); + return this; + } + + /** + * Creates a new {@link Dataset} instance. + */ + public Dataset build() { + return new Dataset(this); + } + + /** + * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService} + * methods such as + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, + * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using + * {@link AutoFillService#EXTRA_DATASET_EXTRAS} as the key. + * + * <p>It can be used to keep service state in between calls. + */ + public Builder setExtras(Bundle extras) { + mExtras = Objects.requireNonNull(extras, "extras cannot be null"); + return this; + } + + /** + * Emulates {@code Map.put()} by adding a new field to the list if its id is not the yet, + * or replacing the existing one. + */ + private void putField(DatasetField field) { + // TODO(b/33197203): check if already exists and replaces it if so + mFields.add(field); + } + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeCharSequence(mName); + parcel.writeList(mFields); + parcel.writeBundle(mExtras); + } + + @SuppressWarnings("unchecked") + private Dataset(Parcel parcel) { + mName = parcel.readCharSequence(); + mFields = parcel.readArrayList(null); + mExtras = parcel.readBundle(); + } + + public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() { + @Override + public Dataset createFromParcel(Parcel source) { + return new Dataset(source); + } + + @Override + public Dataset[] newArray(int size) { + return new Dataset[size]; + } + }; +} diff --git a/core/java/android/view/autofill/DatasetField.java b/core/java/android/view/autofill/DatasetField.java new file mode 100644 index 000000000000..c6b92acd5759 --- /dev/null +++ b/core/java/android/view/autofill/DatasetField.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; + +import android.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public final class DatasetField implements Parcelable { + + private final AutoFillId mId; + private final AutoFillValue mValue; + + DatasetField(AutoFillId id, AutoFillValue value) { + mId = id; + mValue = value; + } + + public AutoFillId getId() { + return mId; + } + + public AutoFillValue getValue() { + return mValue; + } + + ///////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////// + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + return "DatasetField [id=" + mId + ", value=" + mValue + "]"; + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mId, 0); + parcel.writeParcelable(mValue, 0); + } + + private DatasetField(Parcel parcel) { + mId = parcel.readParcelable(null); + mValue = parcel.readParcelable(null); + } + + public static final Parcelable.Creator<DatasetField> CREATOR = + new Parcelable.Creator<DatasetField>() { + @Override + public DatasetField createFromParcel(Parcel source) { + return new DatasetField(source); + } + + @Override + public DatasetField[] newArray(int size) { + return new DatasetField[size]; + } + }; +} diff --git a/core/java/android/view/autofill/FieldId.aidl b/core/java/android/view/autofill/FieldId.aidl new file mode 100644 index 000000000000..35af6453dbd1 --- /dev/null +++ b/core/java/android/view/autofill/FieldId.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016, 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.view.autofill; + +parcelable FieldId;
\ No newline at end of file diff --git a/core/java/android/view/autofill/FillResponse.aidl b/core/java/android/view/autofill/FillResponse.aidl new file mode 100644 index 000000000000..b018f15923ec --- /dev/null +++ b/core/java/android/view/autofill/FillResponse.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2016, 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.view.autofill; + +parcelable FillResponse;
\ No newline at end of file diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java new file mode 100644 index 000000000000..3a147674965e --- /dev/null +++ b/core/java/android/view/autofill/FillResponse.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import static android.view.autofill.Helper.DEBUG; +import static android.view.autofill.Helper.append; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.autofill.AutoFillService; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Response for a + * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, + * android.os.CancellationSignal, android.service.autofill.FillCallback)} + * request. + * + * <p>The response typically contains one or more {@link Dataset}s, each representing a set of + * fields that can be auto-filled together. For example, for a login page with username/password + * where the user only have one account in the service, the response could be: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .add(new Dataset.Builder("homer") + * .setTextFieldValue(id1, "homer") + * .setTextFieldValue(id2, "D'OH!") + * .build()) + * .build(); + * </pre> + * + * <p>If the user had 2 accounts, each with its own user-provided names, the response could be: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .add(new Dataset.Builder("Homer's Account") + * .setTextFieldValue(id1, "homer") + * .setTextFieldValue(id2, "D'OH!") + * .build()) + * .add(new Dataset.Builder("Bart's Account") + * .setTextFieldValue(id1, "elbarto") + * .setTextFieldValue(id2, "cowabonga") + * .build()) + * .build(); + * </pre> + * + * <p>If the user does not have any data associated with this {@link Activity} but the service + * wants to offer the user the option to save the data that was entered, then the service could + * populate the response with {@code savableIds} instead of {@link Dataset}s: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .addSavableFields(id1, id2) + * .build(); + * </pre> + * + * <p>Similarly, there might be cases where the user data on the service is enough to populate some + * fields but not all, and the service would still be interested on saving the other fields. In this + * scenario, the service could populate the response with both {@link Dataset}s and + * {@code savableIds}: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .add(new Dataset.Builder("Homer") + * .setTextFieldValue(id1, "Homer") // first name + * .setTextFieldValue(id2, "Simpson") // last name + * .setTextFieldValue(id3, "742 Evergreen Terrace") // street + * .setTextFieldValue(id4, "Springfield") // city + * .build()) + * .addSavableFields(id5, id6) // state and zipcode + * .build(); + * + * </pre> + * + * <p>Notice that the ids that are part of a dataset (ids 1 to 4, in this example) are automatically + * added to the {@code savableIds} list. + * + * <p>If the service has multiple {@link Dataset}s with multiple options for some fields on each + * dataset (for example, multiple accounts with both a home and work address), then it should + * "partition" the {@link Activity} in sections and populate the response with just a subset of the + * data that would fulfill the first section; then once the user fills the first section and taps + * a field from the next section, the Android system would issue another request for that section, + * and so on. For example, the first response could be: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .add(new Dataset.Builder("Homer") + * .setTextFieldValue(id1, "Homer") + * .setTextFieldValue(id2, "Simpson") + * .build()) + * .add(new Dataset.Builder("Bart") + * .setTextFieldValue(id1, "Bart") + * .setTextFieldValue(id2, "Simpson") + * .build()) + * .build(); + * </pre> + * + * <p>Then after the user picks the {@code Homer} dataset and taps the {@code Street} field to + * trigger another auto-fill request, the second response could be: + * + * <pre class="prettyprint"> + * new FillResponse.Builder() + * .add(new Dataset.Builder("Home") + * .setTextFieldValue(id3, "742 Evergreen Terrace") + * .setTextFieldValue(id4, "Springfield") + * .build()) + * .add(new Dataset.Builder("Work") + * .setTextFieldValue(id3, "Springfield Nuclear Power Plant") + * .setTextFieldValue(id4, "Springfield") + * .build()) + * .build(); + * </pre> + * + * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or + * {@link Dataset.Builder#setExtras(Bundle)} methods to pass + * a {@link Bundle} with service-specific data use to identify this response on future calls (like + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, + * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundle will be + * available as the {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} extra in + * that method's {@code extras} argument. + */ +public final class FillResponse implements Parcelable { + + private final List<Dataset> mDatasets; + private final AutoFillId[] mSavableIds; + private final Bundle mExtras; + + private FillResponse(Builder builder) { + // TODO(b/33197203): make it immutable? + mDatasets = builder.mDatasets; + final int size = builder.mSavableIds.size(); + mSavableIds = new AutoFillId[size]; + int i = 0; + for (AutoFillId id : builder.mSavableIds) { + mSavableIds[i++] = id; + } + mExtras = builder.mExtras; + } + + /** @hide */ + public List<Dataset> getDatasets() { + return mDatasets; + } + + /** @hide */ + public AutoFillId[] getSavableIds() { + return mSavableIds; + } + + /** @hide */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Builder for {@link FillResponse} objects. + */ + public static final class Builder { + private final List<Dataset> mDatasets = new ArrayList<>(); + private final Set<AutoFillId> mSavableIds = new HashSet<>(); + private Bundle mExtras; + + /** + * Adds a new {@link Dataset} to this response. + * + * @throws IllegalArgumentException if a dataset with same {@code name} already exists. + */ + public Builder addDataset(Dataset dataset) { + Preconditions.checkNotNull(dataset, "dataset cannot be null"); + // TODO(b/33197203): check if name already exists + // TODO(b/33197203): check if authId already exists (and update javadoc) + mDatasets.add(dataset); + for (DatasetField field : dataset.getFields()) { + mSavableIds.add(field.getId()); + } + return this; + } + + /** + * Adds ids of additional fields that the service would be interested to save (through + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, + * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) but were not + * indirectly set through {@link #addDataset(Dataset)}. + * + * <p>See {@link FillResponse} for examples. + */ + public Builder addSavableFields(AutoFillId...ids) { + for (AutoFillId id : ids) { + mSavableIds.add(id); + } + return this; + } + + /** + * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService} + * methods such as + * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, + * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using + * {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key. + * + * <p>It can be used when to keep service state in between calls. + */ + public Builder setExtras(Bundle extras) { + mExtras = Objects.requireNonNull(extras, "extras cannot be null"); + return this; + } + + /** + * Builds a new {@link FillResponse} instance. + */ + public FillResponse build() { + return new FillResponse(this); + } + } + + ///////////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////////// + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + final StringBuilder builder = new StringBuilder("FillResponse: [datasets=") + .append(mDatasets).append(", savableIds=").append(Arrays.toString(mSavableIds)) + .append(", extras="); + append(builder, mExtras); + return builder.append(']').toString(); + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeList(mDatasets); + parcel.writeParcelableArray(mSavableIds, 0); + parcel.writeBundle(mExtras); + } + + private FillResponse(Parcel parcel) { + mDatasets = new ArrayList<>(); + parcel.readList(mDatasets, null); + mSavableIds = parcel.readParcelableArray(null, AutoFillId.class); + mExtras = parcel.readBundle(); + } + + public static final Parcelable.Creator<FillResponse> CREATOR = + new Parcelable.Creator<FillResponse>() { + @Override + public FillResponse createFromParcel(Parcel source) { + return new FillResponse(source); + } + + @Override + public FillResponse[] newArray(int size) { + return new FillResponse[size]; + } + }; +} diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java new file mode 100644 index 000000000000..772710e31b88 --- /dev/null +++ b/core/java/android/view/autofill/Helper.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import android.os.Bundle; + +import java.util.Set; + +/** @hide */ +public final class Helper { + + // TODO(b/33197203): set to false when stable + static final boolean DEBUG = true; + static final String REDACTED = "[REDACTED]"; + + static void append(StringBuilder builder, Bundle bundle) { + if (bundle == null) { + builder.append("N/A"); + } else if (!DEBUG) { + builder.append(REDACTED); + } else { + final Set<String> keySet = bundle.keySet(); + builder.append("[bundle with ").append(keySet.size()).append(" extras:"); + for (String key : keySet) { + builder.append(' ').append(key).append('=').append(bundle.get(key)).append(','); + } + builder.append(']'); + } + } + + private Helper() { + throw new UnsupportedOperationException("contains static members only"); + } +} diff --git a/core/java/android/view/autofill/VirtualViewDelegate.java b/core/java/android/view/autofill/VirtualViewDelegate.java new file mode 100644 index 000000000000..a19b4e53ba52 --- /dev/null +++ b/core/java/android/view/autofill/VirtualViewDelegate.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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.view.autofill; + +import android.util.Log; +import android.view.View; +import android.view.ViewStructure; + +/** + * This class is the contract a client should implement to enable support of a + * virtual view hierarchy rooted at a given view for auto-fill purposes. + * + * <p>The view hierarchy is typically created through the + * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)} call and client + * add virtual children by calling {@link ViewStructure#newChild(int, int)} or + * {@link ViewStructure#asyncNewChild(int, int)}, where the client provides the {@code virtualId} + * of the children - the same {@code virtualId} is used in the methods of this class. + * + * <p>Objects of this class are typically created by overriding + * {@link View#getAutoFillVirtualViewDelegate(Callback)} and saving the passed callback, which must + * be notified upon changes on the hierarchy. + * + * <p>The main use case of these API is to enable custom views that draws its content - such as + * {@link android.webkit.WebView} providers - to support the AutoFill Framework: + * + * <ol> + * <li>Client populates the virtual hierarchy on + * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)} + * <li>Android System generates the proper {@link AutoFillId} - encapsulating the view and the + * virtual node ids - and pass it to the {@link android.service.autofill.AutoFillService}. + * <li>The service uses the {@link AutoFillId} to populate the auto-fill {@link Dataset}s and pass + * it back to the Android System. + * <li>Android System uses the {@link AutoFillId} to find the proper custom view and calls + * {@link #autoFill(int, AutoFillValue)} on that view passing the virtual id. + * <li>This provider than finds the node in the hierarchy and auto-fills it. + * </ol> + * + */ +public abstract class VirtualViewDelegate { + + // TODO(b/33197203): set to false once stable + private static final boolean DEBUG = true; + + private static final String TAG = "VirtualViewDelegate"; + + /** + * Auto-fills a virtual view with the {@code value}. + * + * @param virtualId id identifying the virtual node inside the custom view. + * @param value value to be auto-filled. + */ + public abstract void autoFill(int virtualId, AutoFillValue value); + + /** + * Callback used to notify the AutoFill Framework of changes made on the view hierarchy while + * an {@link android.app.Activity} is being auto filled. + */ + public abstract static class Callback { + + /** + * Sent when the focus inside the hierarchy changed. + * + * <p>Typically callled twice - for the nodes that lost and gained focus. + * + * <p>This method should only be called when the change was not caused by the AutoFill + * Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}, + * but by external causes (for example, when the user changed the value through the view's + * UI). + * + * @param virtualId id of the node whose focus changed. + * @param hasFocus {@code true} when focus was gained, {@code false} when it was lost. + */ + public void onFocusChanged(int virtualId, boolean hasFocus) { + if (DEBUG) Log.d(TAG, "onFocusChanged() for " + virtualId + ": " + hasFocus); + } + + /** + * Sent when the value of a node was changed. + * + * <p>This method should only be called when the change was not caused by the AutoFill + * Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}, + * but by external causes (for example, when the user changed the value through the view's + * UI). + * + * @param virtualId id of the node whose value changed. + */ + public void onValueChanged(int virtualId) { + if (DEBUG) Log.d(TAG, "onValueChanged() for" + virtualId); + } + + /** + * Sent when nodes were removed (or had their ids changed) after the hierarchy has been + * committed to + * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)}. + * + * <p>For example, when the view is rendering an {@code HTML} page, it should call this + * method when: + * <ul> + * <li>User navigated to another page and some (or all) nodes are gone. + * <li>The page's {@code DOM} was changed by {@code JavaScript} and some nodes moved (and + * are now identified by different ids). + * </ul> + * + * @param virtualIds id of the nodes that were removed. + */ + public void onNodeRemoved(int... virtualIds) { + if (DEBUG) Log.d(TAG, "onNodeRemoved(): " + virtualIds); + } + } +} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 8ecc42de12df..f98c099f053f 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2515,8 +2515,8 @@ public class WebView extends AbsoluteLayout } @Override - public void onProvideVirtualStructure(ViewStructure structure, int flags) { - mProvider.getViewDelegate().onProvideVirtualStructure(structure, flags); + public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { + mProvider.getViewDelegate().onProvideAutoFillVirtualStructure(structure, flags); } /** @hide */ diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index eb0c44c3a07d..d7a49e46a236 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -59,8 +59,8 @@ public final class WebViewFactory { // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. /** @hide */ - public static final String CHROMIUM_WEBVIEW_FACTORY = - "com.android.webview.chromium.WebViewChromiumFactoryProvider"; + private static final String CHROMIUM_WEBVIEW_FACTORY = + "com.android.webview.chromium.WebViewChromiumFactoryProviderForO"; private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; @@ -143,6 +143,23 @@ public final class WebViewFactory { } /** + * @hide + */ + public static Class<WebViewFactoryProvider> getWebViewProviderClass( ClassLoader clazzLoader) + throws ClassNotFoundException{ + try { + return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, + true, clazzLoader); + } catch (ClassNotFoundException e) { + // TODO: This loads the provider which is not built for O, should be removed + // before the release. + return (Class<WebViewFactoryProvider>) Class.forName( + "com.android.webview.chromium.WebViewChromiumFactoryProvider", + true, clazzLoader); + } + } + + /** * Load the native library for the given package name iff that package * name is the same as the one providing the webview. */ @@ -366,9 +383,9 @@ public final class WebViewFactory { Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()"); try { - return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, - true, clazzLoader); - } finally { + return getWebViewProviderClass(clazzLoader); + } + finally { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } } catch (ClassNotFoundException e) { diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 7b951806f097..dd1b0d2d17a4 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -312,7 +312,7 @@ public interface WebViewProvider { public void onProvideVirtualStructure(android.view.ViewStructure structure); @SuppressWarnings("unused") - public default void onProvideVirtualStructure(android.view.ViewStructure structure, + public default void onProvideAutoFillVirtualStructure(android.view.ViewStructure structure, int flags) { } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 3213a34e28f1..718070d0d49a 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -34,6 +34,8 @@ import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; import com.android.internal.R; @@ -52,6 +54,7 @@ import com.android.internal.R; * </p> */ public abstract class CompoundButton extends Button implements Checkable { + private boolean mChecked; private boolean mBroadcasting; @@ -111,6 +114,7 @@ public abstract class CompoundButton extends Button implements Checkable { applyButtonTint(); } + @Override public void toggle() { setChecked(!mChecked); } @@ -130,6 +134,7 @@ public abstract class CompoundButton extends Button implements Checkable { } @ViewDebug.ExportedProperty + @Override public boolean isChecked() { return mChecked; } @@ -139,6 +144,7 @@ public abstract class CompoundButton extends Button implements Checkable { * * @param checked true to check the button, false to uncheck it */ + @Override public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; @@ -514,12 +520,15 @@ public abstract class CompoundButton extends Button implements Checkable { + " checked=" + checked + "}"; } - public static final Parcelable.Creator<SavedState> CREATOR - = new Parcelable.Creator<SavedState>() { + @SuppressWarnings("hiding") + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } + @Override public SavedState[] newArray(int size) { return new SavedState[size]; } @@ -551,4 +560,16 @@ public abstract class CompoundButton extends Button implements Checkable { super.encodeProperties(stream); stream.addProperty("checked", isChecked()); } + + // TODO(b/33197203): add unit/CTS tests for auto-fill methods + + @Override + public void autoFill(AutoFillValue value) { + setChecked(value.getToggleValue()); + } + + @Override + public AutoFillType getAutoFillType() { + return AutoFillType.forToggle(); + } } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 043eb348a706..af5c8426a44a 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -24,8 +24,10 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; +import android.util.Log; import android.view.accessibility.AccessibilityNodeInfo; - +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; /* * This is supposed to be a *very* thin veneer over TextView. @@ -154,4 +156,26 @@ public class EditText extends TextView { info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT); } } + + // TODO(b/33197203): add unit/CTS tests for auto-fill methods + + @Override + public void autoFill(AutoFillValue value) { + final CharSequence text = value.getTextValue(); + + if (text == null) { + Log.w(VIEW_LOG_TAG, "EditText.autoFill(): no text on AutoFillValue"); + return; + } + // TODO(b/33197203): once auto-fill is triggered by the IME, we'll need a new setText() + // or setAutoFillText() method on TextView to avoid re-triggering it. + setText(text); + } + + @Override + public AutoFillType getAutoFillType() { + // TODO(b/33197203): ideally it should return a constant, but value returned by + // getInputType() can change. + return AutoFillType.forText(getInputType()); + } } diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 54b57631366f..45fd9e6ef5a7 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -16,12 +16,16 @@ package android.widget; + import android.annotation.IdRes; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; import com.android.internal.R; @@ -51,6 +55,7 @@ import com.android.internal.R; * */ public class RadioGroup extends LinearLayout { + // holds the checked id; the selection is empty by default private int mCheckedId = -1; // tracks children radio buttons checked state @@ -335,6 +340,7 @@ public class RadioGroup extends LinearLayout { } private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener { + @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // prevents from infinite recursion if (mProtectFromCheckedChange) { @@ -364,6 +370,7 @@ public class RadioGroup extends LinearLayout { /** * {@inheritDoc} */ + @Override public void onChildViewAdded(View parent, View child) { if (parent == RadioGroup.this && child instanceof RadioButton) { int id = child.getId(); @@ -384,6 +391,7 @@ public class RadioGroup extends LinearLayout { /** * {@inheritDoc} */ + @Override public void onChildViewRemoved(View parent, View child) { if (parent == RadioGroup.this && child instanceof RadioButton) { ((RadioButton) child).setOnCheckedChangeWidgetListener(null); @@ -394,4 +402,22 @@ public class RadioGroup extends LinearLayout { } } } + + // TODO(b/33197203): add unit/CTS tests for auto-fill methods + + @Override + public void autoFill(AutoFillValue value) { + final int index = value.getListValue(); + final View child = getChildAt(index); + if (child == null) { + Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index); + return; + } + check(child.getId()); + } + + @Override + public AutoFillType getAutoFillType() { + return AutoFillType.forList(); + } } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index e629df9bd3df..a9257e6014cf 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -1404,10 +1404,19 @@ public class Switch extends CompoundButton { } @Override - public void onProvideStructure(ViewStructure structure, int flags) { - super.onProvideStructure(structure, flags); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); + onProvideAutoFillStructureForAssistOrAutoFill(structure); + } + + @Override + public void onProvideAutoFillStructure(ViewStructure structure, int flags) { + super.onProvideAutoFillStructure(structure, flags); + onProvideAutoFillStructureForAssistOrAutoFill(structure); + } - // NOTE: current there is no difference for Assist (flags=0) or AutoFill (flags>0); + // NOTE: currently there is no difference for Assist or AutoFill, so it doesn't take flags + private void onProvideAutoFillStructureForAssistOrAutoFill(ViewStructure structure) { CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { CharSequence oldText = structure.getText(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1961bf6b7e15..1ddf53d8b19a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -596,6 +596,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mBreakStrategy; private int mHyphenationFrequency; + private boolean mJustify; private int mMaximum = Integer.MAX_VALUE; private int mMaxMode = LINES; @@ -769,6 +770,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener String fontFeatureSettings = null; mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; + mJustify = false; final Resources.Theme theme = context.getTheme(); @@ -3298,6 +3300,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Enables or disables full justification. The default value is false. + * + * @see #getJustify() + */ + public void setJustify(boolean justify) { + mJustify = justify; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * @return true if currently paragraph justification is enabled. + * + * @see #setJustify(boolean) + */ + public boolean getJustify() { + return mJustify; + } + + /** * Sets font feature settings. The format is the same as the CSS * font-feature-settings attribute: * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop"> @@ -7170,6 +7195,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy) .setHyphenationFrequency(mHyphenationFrequency) + .setJustify(mJustify) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); if (shouldEllipsize) { builder.setEllipsize(mEllipsize) @@ -7211,7 +7237,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, - mBreakStrategy, mHyphenationFrequency, + mBreakStrategy, mHyphenationFrequency, mJustify, getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -7261,6 +7287,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setIncludePad(mIncludePad) .setBreakStrategy(mBreakStrategy) .setHyphenationFrequency(mHyphenationFrequency) + .setJustify(mJustify) .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); if (shouldEllipsize) { builder.setEllipsize(effectiveEllipsize) @@ -9391,11 +9418,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void onProvideStructure(ViewStructure structure, int flags) { - super.onProvideStructure(structure, flags); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); + onProvideAutoStructureForAssistOrAutoFill(structure, 0); + } + @Override + public void onProvideAutoFillStructure(ViewStructure structure, int flags) { + super.onProvideAutoFillStructure(structure, flags); + onProvideAutoStructureForAssistOrAutoFill(structure, flags); + } + + private void onProvideAutoStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, + // this method should take a boolean with the type of request. final boolean forAutoFillSave = - (flags & ASSIST_FLAG_NON_SANITIZED_TEXT) != 0; + (flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0; + final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); if (!isPassword || forAutoFillSave) { diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java index 634d061e79d1..8e2e114be454 100644 --- a/core/java/com/android/internal/logging/LogBuilder.java +++ b/core/java/com/android/internal/logging/LogBuilder.java @@ -17,8 +17,9 @@ public class LogBuilder { private SparseArray<Object> entries = new SparseArray(); - public LogBuilder() {} - + public LogBuilder(int mainCategory) { + setCategory(mainCategory); + } public LogBuilder setView(View view) { entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VIEW, view.getId()); diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index 12d699d2ae15..d82a211446d3 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -24,6 +24,7 @@ import android.system.Os; import android.text.TextUtils; import android.util.Log; import android.webkit.WebViewFactory; +import android.webkit.WebViewFactoryProvider; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -74,8 +75,8 @@ class WebViewZygoteInit { // call preloadInZygote() on it to give it the opportunity to preload the native library // and perform any other initialisation work that should be shared among the children. try { - Class providerClass = Class.forName(WebViewFactory.CHROMIUM_WEBVIEW_FACTORY, true, - loader); + Class<WebViewFactoryProvider> providerClass = + WebViewFactory.getWebViewProviderClass(loader); Object result = providerClass.getMethod("preloadInZygote").invoke(null); if (!((Boolean)result).booleanValue()) { Log.e(TAG, "preloadInZygote returned false"); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 293de3d71332..e1e0a21eb7f5 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -85,6 +85,9 @@ public final class Zygote { * file descriptor numbers that are to be closed by the child * (and replaced by /dev/null) after forking. An integer value * of -1 in any entry in the array means "ignore this one". + * @param fdsToIgnore null-ok an array of ints, either null or holding + * one or more POSIX file descriptor numbers that are to be ignored + * in the file descriptor table check. * @param instructionSet null-ok the instruction set to use. * @param appDataDir null-ok the data directory of the app. * @@ -93,11 +96,11 @@ public final class Zygote { */ public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - String instructionSet, String appDataDir) { + int[] fdsToIgnore, String instructionSet, String appDataDir) { VM_HOOKS.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, - instructionSet, appDataDir); + fdsToIgnore, instructionSet, appDataDir); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true); @@ -111,7 +114,7 @@ public final class Zygote { native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - String instructionSet, String appDataDir); + int[] fdsToIgnore, String instructionSet, String appDataDir); /** * Special method to start the system server process. In addition to the diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 345350cc45fb..83e3cff360f4 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -196,11 +196,14 @@ class ZygoteConnection { rlimits = parsedArgs.rlimits.toArray(intArray2d); } + int[] fdsToIgnore = null; + if (parsedArgs.invokeWith != null) { FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; Os.fcntlInt(childPipeFd, F_SETFD, 0); + fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() }; } /** @@ -233,7 +236,7 @@ class ZygoteConnection { pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, - parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet, + parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, parsedArgs.appDataDir); } catch (ErrnoException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java index b41ef297a5be..32ce0fe1282b 100644 --- a/core/java/com/android/internal/view/BaseSurfaceHolder.java +++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java @@ -86,7 +86,7 @@ public abstract class BaseSurfaceHolder implements SurfaceHolder { mCallbacks.remove(callback); } } - + public SurfaceHolder.Callback[] getCallbacks() { if (mHaveGottenCallbacks) { return mGottenCallbacks; diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 4f7b106a83fe..0185e306118f 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -562,8 +562,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } case DO_COMMIT_CONTENT: { final int flags = msg.arg1; - final boolean grantUriPermission = - (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0; SomeArgs args = (SomeArgs) msg.obj; try { InputConnection ic = getInputConnection(); @@ -579,22 +577,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { args.callback.setCommitContentResult(false, args.seq); return; } - if (grantUriPermission) { - try { - inputContentInfo.requestPermission(); - } catch (Exception e) { - Log.e(TAG, "InputConnectionInfo.requestPermission() failed", e); - args.callback.setCommitContentResult(false, args.seq); - return; - } - } final boolean result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); - // If this request is not handled, then there is no reason to keep the URI - // permission. - if (grantUriPermission && !result) { - inputContentInfo.releasePermission(); - } args.callback.setCommitContentResult(result, args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling commitContent", e); diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java new file mode 100644 index 000000000000..5b6a82cf1c43 --- /dev/null +++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 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.internal.view; + +import android.os.RemoteException; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.Surface; +import android.view.SurfaceHolder; + +public class SurfaceCallbackHelper { + IWindowSession mSession; + IWindow.Stub mWindow; + + int mFinishDrawingCollected = 0; + int mFinishDrawingExpected = 0; + + private Runnable mFinishDrawingRunnable = new Runnable() { + @Override + public void run() { + synchronized (SurfaceCallbackHelper.this) { + mFinishDrawingCollected++; + if (mFinishDrawingCollected < mFinishDrawingExpected) { + return; + } + try { + mSession.finishDrawing(mWindow); + } catch (RemoteException e) { + } + } + } + }; + + public SurfaceCallbackHelper(IWindowSession session, + IWindow.Stub window) { + mSession = session; + mWindow = window; + } + + public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) { + if (callbacks == null || callbacks.length == 0) { + try { + mSession.finishDrawing(mWindow); + } catch (RemoteException e) { + } + return; + } + + synchronized (this) { + mFinishDrawingExpected = callbacks.length; + mFinishDrawingCollected = 0; + } + + for (SurfaceHolder.Callback c : callbacks) { + if (c instanceof SurfaceHolder.Callback2) { + ((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync( + holder, mFinishDrawingRunnable); + } else { + mFinishDrawingRunnable.run(); + } + } + } +}
\ No newline at end of file diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp index aa6348ee2a31..dcb787462a13 100644 --- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp +++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp @@ -33,7 +33,9 @@ #include "HarfBuzzNGFaceSkia.h" #include <stdlib.h> -#include <cutils/log.h> + +#include <log/log.h> + #include <SkPaint.h> #include <SkPath.h> #include <SkPoint.h> diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index aa4570ff8d70..14d7e812d583 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -16,32 +16,32 @@ #define LOG_TAG "android.os.Debug" +#include <assert.h> +#include <ctype.h> +#include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <malloc.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <time.h> #include <sys/time.h> -#include <errno.h> -#include <assert.h> -#include <ctype.h> -#include <malloc.h> +#include <time.h> +#include <unistd.h> #include <iomanip> #include <string> -#include "jni.h" +#include <android-base/stringprintf.h> +#include <cutils/debugger.h> +#include <log/log.h> +#include <utils/misc.h> +#include <utils/String8.h> -#include "android-base/stringprintf.h" -#include "cutils/debugger.h" -#include "cutils/log.h" #include "JNIHelp.h" -#include "memtrack/memtrack.h" -#include "memunreachable/memunreachable.h" -#include "utils/misc.h" -#include "utils/String8.h" +#include "jni.h" +#include <memtrack/memtrack.h> +#include <memunreachable/memunreachable.h> namespace android { diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index ea893f0dd1bb..de91f70ee8f5 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -19,15 +19,14 @@ #include <inttypes.h> +#include <cutils/trace.h> +#include <utils/String8.h> +#include <log/log.h> + #include <JNIHelp.h> #include <ScopedUtfChars.h> #include <ScopedStringChars.h> -#include <utils/String8.h> - -#include <cutils/trace.h> -#include <cutils/log.h> - namespace android { static void sanitizeString(String8& utf8Chars) { diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 02fa8720d5d2..c05ef26fd5ac 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -54,7 +54,8 @@ static JLineBreaksID gLineBreaks_fieldID; // hyphenFrequency) static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length, jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth, - jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) { + jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency, + jboolean isJustified) { minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); b->resize(length); env->GetCharArrayRegion(text, 0, length, b->buffer()); @@ -68,6 +69,7 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex } b->setStrategy(static_cast<minikin::BreakStrategy>(strategy)); b->setHyphenationFrequency(static_cast<minikin::HyphenationFrequency>(hyphenFrequency)); + b->setJustified(isJustified); } static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, @@ -190,7 +192,7 @@ static const JNINativeMethod gMethods[] = { {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator}, {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale}, - {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph}, + {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph}, {"nSetIndents", "(J[I)V", (void*) nSetIndents}, {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun}, {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun}, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 5559d48a58b6..abcd1e7049ef 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -29,19 +29,19 @@ #include <sys/types.h> #include <unistd.h> -#include <utils/Atomic.h> #include <binder/IInterface.h> +#include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> -#include <utils/Log.h> -#include <utils/SystemClock.h> -#include <utils/List.h> -#include <utils/KeyedVector.h> -#include <log/logger.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> -#include <binder/IServiceManager.h> -#include <utils/threads.h> +#include <log/log.h> +#include <utils/Atomic.h> +#include <utils/KeyedVector.h> +#include <utils/List.h> +#include <utils/Log.h> #include <utils/String8.h> +#include <utils/SystemClock.h> +#include <utils/threads.h> #include <ScopedUtfChars.h> #include <ScopedLocalRef.h> diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp index 7719e31f4240..20dfe7809728 100644 --- a/core/jni/android_util_Log.cpp +++ b/core/jni/android_util_Log.cpp @@ -21,7 +21,7 @@ #include <android-base/macros.h> #include <assert.h> #include <cutils/properties.h> -#include <log/logger.h> // For LOGGER_ENTRY_MAX_PAYLOAD. +#include <log/log.h> // For LOGGER_ENTRY_MAX_PAYLOAD. #include <utils/Log.h> #include <utils/String8.h> diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp index 2e31c8b2fc8f..4f1f926c8a22 100644 --- a/core/jni/android_util_jar_StrictJarFile.cpp +++ b/core/jni/android_util_jar_StrictJarFile.cpp @@ -20,13 +20,14 @@ #include <memory> #include <string> +#include <log/log.h> + #include "JNIHelp.h" #include "JniConstants.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" #include "jni.h" #include "ziparchive/zip_archive.h" -#include "cutils/log.h" namespace android { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index cc7b95894a1f..070a2d96cd40 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -43,6 +43,7 @@ #include <sys/wait.h> #include <unistd.h> +#include "android-base/logging.h" #include <cutils/fs.h> #include <cutils/multiuser.h> #include <cutils/sched_policy.h> @@ -440,6 +441,22 @@ void SetThreadName(const char* thread_name) { // The list of open zygote file descriptors. static FileDescriptorTable* gOpenFdTable = NULL; +static void FillFileDescriptorVector(JNIEnv* env, + jintArray java_fds, + std::vector<int>* fds) { + CHECK(fds != nullptr); + if (java_fds != nullptr) { + ScopedIntArrayRO ar(env, java_fds); + if (ar.get() == nullptr) { + RuntimeAbort(env, __LINE__, "Bad fd array"); + } + fds->reserve(ar.size()); + for (size_t i = 0; i < ar.size(); ++i) { + fds->push_back(ar[i]); + } + } +} + // Utility routine to fork zygote and specialize the child process. static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, jint debug_flags, jobjectArray javaRlimits, @@ -447,6 +464,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra jint mount_external, jstring java_se_info, jstring java_se_name, bool is_system_server, jintArray fdsToClose, + jintArray fdsToIgnore, jstring instructionSet, jstring dataDir) { SetSigChldHandler(); @@ -471,12 +489,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra // If this is the first fork for this zygote, create the open FD table. // If it isn't, we just need to check whether the list of open files has // changed (and it shouldn't in the normal case). + std::vector<int> fds_to_ignore; + FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore); if (gOpenFdTable == NULL) { - gOpenFdTable = FileDescriptorTable::Create(); + gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore); if (gOpenFdTable == NULL) { RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table."); } - } else if (!gOpenFdTable->Restat()) { + } else if (!gOpenFdTable->Restat(fds_to_ignore)) { RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table."); } @@ -646,7 +666,9 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint debug_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring se_name, - jintArray fdsToClose, jstring instructionSet, jstring appDataDir) { + jintArray fdsToClose, + jintArray fdsToIgnore, + jstring instructionSet, jstring appDataDir) { jlong capabilities = 0; // Grant CAP_WAKE_ALARM to the Bluetooth process. @@ -681,7 +703,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, capabilities, capabilities, mount_external, se_info, - se_name, false, fdsToClose, instructionSet, appDataDir); + se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir); } static jint com_android_internal_os_Zygote_nativeForkSystemServer( @@ -692,7 +714,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( debug_flags, rlimits, permittedCapabilities, effectiveCapabilities, MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL, - NULL, NULL); + NULL, NULL, NULL); if (pid > 0) { // The zygote process checks whether the child process has died or not. ALOGI("System server process %d has been created", pid); @@ -759,7 +781,7 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en static const JNINativeMethod gMethods[] = { { "nativeForkAndSpecialize", - "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I", + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I", (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize }, { "nativeForkSystemServer", "(II[II[[IJJ)I", (void *) com_android_internal_os_Zygote_nativeForkSystemServer }, diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 969d336f3cad..59a536b1b001 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -381,7 +381,7 @@ bool FileDescriptorInfo::DetachSocket() const { } // static -FileDescriptorTable* FileDescriptorTable::Create() { +FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) { DIR* d = opendir(kFdPath); if (d == NULL) { ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno)); @@ -396,6 +396,10 @@ FileDescriptorTable* FileDescriptorTable::Create() { if (fd == -1) { continue; } + if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) { + ALOGI("Ignoring open file descriptor %d", fd); + continue; + } FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd); if (info == NULL) { @@ -414,7 +418,7 @@ FileDescriptorTable* FileDescriptorTable::Create() { return new FileDescriptorTable(open_fd_map); } -bool FileDescriptorTable::Restat() { +bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) { std::set<int> open_fds; // First get the list of open descriptors. @@ -431,6 +435,10 @@ bool FileDescriptorTable::Restat() { if (fd == -1) { continue; } + if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) { + ALOGI("Ignoring open file descriptor %d", fd); + continue; + } open_fds.insert(fd); } diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h index 9e3afd910914..03298c38dca9 100644 --- a/core/jni/fd_utils.h +++ b/core/jni/fd_utils.h @@ -122,9 +122,9 @@ class FileDescriptorTable { // Creates a new FileDescriptorTable. This function scans // /proc/self/fd for the list of open file descriptors and collects // information about them. Returns NULL if an error occurs. - static FileDescriptorTable* Create(); + static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore); - bool Restat(); + bool Restat(const std::vector<int>& fds_to_ignore); // Reopens all file descriptors that are contained in the table. Returns true // if all descriptors were successfully re-opened or detached, and false if an diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9cbb8c3b0167..89581bbb3ac4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1049,6 +1049,16 @@ Must be in the range specified by minimum and maximum. --> <integer name="config_screenBrightnessSettingDefault">102</integer> + <!-- Default screen brightness for VR setting. --> + <integer name="config_screenBrightnessForVrSettingDefault">86</integer> + + <!-- Minimum screen brightness setting allowed for VR. Device panels start increasing pulse + width as brightness decreases below this theshold. --> + <integer name="config_screenBrightnessForVrSettingMinimum">79</integer> + + <!-- Maximum screen brightness setting allowed for VR. --> + <integer name="config_screenBrightnessForVrSettingMaximum">255</integer> + <!-- Screen brightness used to dim the screen while dozing in a very low power state. May be less than the minimum allowed brightness setting that can be set by the user. --> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 5c165e6709ee..55477063a71a 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -129,4 +129,6 @@ <item type="id" name="remote_input_tag" /> <item type="id" name="cross_task_transition" /> + + <item type="id" name="accessibilityActionClickOnClickableSpan" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b7f5a9bb3a13..b0d6457720f4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2868,17 +2868,6 @@ <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. --> <string name="ringtone_unknown">Unknown</string> - <!-- A notification is shown when there are open wireless networks nearby. This is the notification's title. --> - <plurals name="wifi_available"> - <item quantity="one">Wi-Fi network available</item> - <item quantity="other">Wi-Fi networks available</item> - </plurals> - <!-- A notification is shown when there are open wireless networks nearby. This is the notification's message. --> - <plurals name="wifi_available_detailed"> - <item quantity="one">Open Wi-Fi network available</item> - <item quantity="other">Open Wi-Fi networks available</item> - </plurals> - <!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. --> <string name="wifi_available_sign_in">Sign in to Wi-Fi network</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cd3c0e358cb5..f44b0397322b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -213,6 +213,7 @@ <java-symbol type="id" name="selection_start_handle" /> <java-symbol type="id" name="selection_end_handle" /> <java-symbol type="id" name="insertion_handle" /> + <java-symbol type="id" name="accessibilityActionClickOnClickableSpan" /> <java-symbol type="attr" name="actionModeShareDrawable" /> <java-symbol type="attr" name="alertDialogCenterButtons" /> @@ -1803,6 +1804,9 @@ <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" /> <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" /> <java-symbol type="integer" name="config_screenBrightnessSettingDefault" /> + <java-symbol type="integer" name="config_screenBrightnessForVrSettingDefault" /> + <java-symbol type="integer" name="config_screenBrightnessForVrSettingMaximum" /> + <java-symbol type="integer" name="config_screenBrightnessForVrSettingMinimum" /> <java-symbol type="integer" name="config_screenBrightnessDark" /> <java-symbol type="integer" name="config_screenBrightnessDim" /> <java-symbol type="integer" name="config_screenBrightnessDoze" /> @@ -1816,8 +1820,6 @@ <java-symbol type="layout" name="safe_mode" /> <java-symbol type="layout" name="simple_list_item_2_single_choice" /> <java-symbol type="layout" name="app_error_dialog" /> - <java-symbol type="plurals" name="wifi_available" /> - <java-symbol type="plurals" name="wifi_available_detailed" /> <java-symbol type="string" name="accessibility_binding_label" /> <java-symbol type="string" name="adb_active_notification_message" /> <java-symbol type="string" name="adb_active_notification_title" /> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index e3a85b562068..504541d9522b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -55,6 +55,8 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> + <uses-permission android:name="android.permission.READ_DREAM_STATE" /> + <uses-permission android:name="android.permission.WRITE_DREAM_STATE" /> <uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_SMS"/> @@ -831,14 +833,14 @@ </intent-filter> </activity> - <activity android:name="android.widget.listview.ListManagedCursor" android:label="ListManagedCursor"> + <activity android:name="android.widget.listview.ListManagedCursor" android:label="ListManagedCursor"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> </intent-filter> </activity> - <activity android:name="android.widget.listview.ListWithEmptyView" android:label="ListWithEmptyView"> + <activity android:name="android.widget.listview.ListWithEmptyView" android:label="ListWithEmptyView"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> @@ -1331,6 +1333,27 @@ </meta-data> </service> + <activity + android:name="android.print.mockservice.SettingsActivity" + android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY" + android:exported="true"> + </activity> + + <activity + android:name="android.os.TestVrActivity" + android:enableVrMode="com.android.frameworks.coretests/android.os.TestVrActivity$TestVrListenerService"> + </activity> + + <service android:name="android.os.TestVrActivity$TestVrListenerService" + android:exported="true" + android:enabled="true" + android:label="Test Vr Listener Service" + android:permission="android.permission.BIND_VR_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.vr.VrListenerService" /> + </intent-filter> + </service> + </application> <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" diff --git a/core/tests/coretests/src/android/os/PowerManagerVrTest.java b/core/tests/coretests/src/android/os/PowerManagerVrTest.java new file mode 100644 index 000000000000..e01e5fa4faee --- /dev/null +++ b/core/tests/coretests/src/android/os/PowerManagerVrTest.java @@ -0,0 +1,149 @@ +/* + * 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.os; + +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.service.dreams.IDreamManager; +import android.service.dreams.DreamService; +import android.support.test.filters.MediumTest; +import android.support.test.filters.SmallTest; +import android.test.ActivityInstrumentationTestCase2; + +/** + * Tests dream aspects of PowerManager. + */ +@MediumTest +public class PowerManagerVrTest extends ActivityInstrumentationTestCase2<TestVrActivity> { + private PowerManager mPm; + private IDreamManager mDm; + private String mOldVrListener; + + public PowerManagerVrTest() { + super(TestVrActivity.class); + } + + /** + * Setup any common data for the upcoming tests. + */ + @Override + public void setUp() throws Exception { + super.setUp(); + Context context = getInstrumentation().getTargetContext(); + mPm = (PowerManager) getInstrumentation().getTargetContext().getSystemService( + Context.POWER_SERVICE); + mDm = IDreamManager.Stub.asInterface( + ServiceManager.getService(DreamService.DREAM_SERVICE)); + + mOldVrListener = setTestVrListener(new ComponentName( + context, TestVrActivity.TestVrListenerService.class).flattenToString()); + } + + @Override + public void tearDown() throws Exception { + if (mDm != null) { + mDm.awaken(); // Don't leave the device in the dream state. + } + + setTestVrListener(mOldVrListener); + } + + /** + * Confirm that the setup is good. + * + * @throws Exception + */ + @SmallTest + public void testPreconditions() throws Exception { + assertNotNull(mPm); + assertNotNull(mDm); + } + + /** + * Confirm that the system prevents napping while in VR. + * Dreaming is controlled by PowerManager, but we use dreamManager to access those features + * in order to not require DEVICE_POWER permissions which other tests expect not to have. + * + * @throws Exception + */ + @SmallTest + public void testNap() throws Exception { + // For dream to work, we need to wake up the system + wakeUpDevice(); + + mDm.dream(); + waitForDreamState(true); + assertTrue(mDm.isDreaming()); + mDm.awaken(); + + // awaken() is not immediate so we have to wait for dreaming to stop + // before continuing with the test. + waitForDreamState(false); + + // set VR Mode to true by starting our VR Activity, then retest the dream. + TestVrActivity activity = getActivity(); + assertTrue(activity.waitForActivityStart()); + + try { + mDm.dream(); + waitForDreamState(true); // wait for dream to turn true with a timeout + assertFalse(mDm.isDreaming()); // ensure dream is still false after waiting. + mDm.awaken(); + } finally { + activity.finish(); + } + } + + /** + * Waits synchronously for the system to be set to the specified dream state. + */ + private void waitForDreamState(boolean isDreaming) throws Exception { + final int MAX_ATTEMPTS = 10; + final int WAIT_TIME_PER_ATTEMPT_MILLIS = 100; + for (int i = 0; i < MAX_ATTEMPTS; i++) { + if (mDm.isDreaming() == isDreaming) { + break; + } + Thread.sleep(WAIT_TIME_PER_ATTEMPT_MILLIS); + } + } + + private void wakeUpDevice() { + PowerManager.WakeLock wl = mPm.newWakeLock( + PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP + | PowerManager.ON_AFTER_RELEASE, "FULL_WAKE_LOCK"); + wl.acquire(); + wl.release(); + } + + /** + * Sets a new value for the enabled VrListenerService and returns the previous value. + */ + private String setTestVrListener(String newValue) { + final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; + Context context = getInstrumentation().getTargetContext(); + ContentResolver cr = context.getContentResolver(); + String oldVrListeners = Settings.Secure.getString(cr, ENABLED_VR_LISTENERS); + Settings.Secure.putString(cr, ENABLED_VR_LISTENERS, newValue); + return oldVrListeners; + } +} diff --git a/core/tests/coretests/src/android/os/TestVrActivity.java b/core/tests/coretests/src/android/os/TestVrActivity.java new file mode 100644 index 000000000000..33ff1645b37b --- /dev/null +++ b/core/tests/coretests/src/android/os/TestVrActivity.java @@ -0,0 +1,59 @@ +/* + * 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.os; + +import android.app.Activity; +import android.os.Bundle; +import android.service.vr.VrListenerService; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * An activity for enabling/disabling VrMode. + */ +public class TestVrActivity extends Activity { + private CountDownLatch mLatch; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mLatch = new CountDownLatch(1); + } + + @Override + protected void onStart() { + super.onStart(); + mLatch.countDown(); + } + + public static class TestVrListenerService extends VrListenerService { + @Override + public void onCreate() { + super.onCreate(); + } + } + + public boolean waitForActivityStart() { + boolean result = false; + try { + result = mLatch.await(2L, TimeUnit.SECONDS); + } catch (InterruptedException e) { + } + return result; + } +} diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java new file mode 100644 index 000000000000..1e55fb182b4a --- /dev/null +++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java @@ -0,0 +1,143 @@ +/* + * 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.widget; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; +import android.view.ViewGroup.OnHierarchyChangeListener; + +import com.android.frameworks.coretests.R; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +/** + * Tests for AppWidgetHostView + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AppWidgetHostViewTest { + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private Context mContext; + private String mPackage; + private AppWidgetHostView mHostView; + + private ViewAddListener mViewAddListener; + private RemoteViews mViews; + + @Before + public void setup() { + mContext = InstrumentationRegistry.getContext(); + mPackage = mContext.getPackageName(); + mHostView = new AppWidgetHostView(mContext); + mHostView.setAppWidget(0, AppWidgetManager.getInstance( + mContext).getInstalledProviders().get(0)); + + mViewAddListener = new ViewAddListener(); + mHostView.setOnHierarchyChangeListener(mViewAddListener); + + mViews = new RemoteViews(mPackage, R.layout.remote_views_test); + } + + @Test + public void syncInflation() { + mHostView.updateAppWidget(mViews); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + @Test + public void asyncInflation() throws Exception { + RunnableList executor = new RunnableList(); + mHostView.setAsyncExecutor(executor); + + mHostView.updateAppWidget(mViews); + assertNull(mHostView.findViewById(R.id.image)); + + // Task queued. + assertEquals(1, executor.size()); + + // Execute the pending task + executor.get(0).run(); + mViewAddListener.addLatch.await(); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + @Test + public void asyncInflation_cancelled() throws Exception { + RunnableList executor = new RunnableList(); + mHostView.setAsyncExecutor(executor); + + mHostView.updateAppWidget(mViews.clone()); + mHostView.updateAppWidget(mViews.clone()); + assertNull(mHostView.findViewById(R.id.image)); + + // Tasks queued. + assertEquals(2, executor.size()); + // First task cancelled + assertTrue(((Future) executor.get(0)).isCancelled()); + + // Execute the pending task + executor.get(0).run(); + executor.get(1).run(); + mViewAddListener.addLatch.await(); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + private static class RunnableList extends ArrayList<Runnable> implements Executor { + + @Override + public void execute(Runnable runnable) { + add(runnable); + } + } + + private class ViewAddListener implements OnHierarchyChangeListener { + + public final CountDownLatch addLatch = new CountDownLatch(1); + + + @Override + public void onChildViewAdded(View parent, View child) { + addLatch.countDown(); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java index e7d23a8edb9c..e3f754ca8925 100644 --- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java +++ b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java @@ -5,7 +5,7 @@ import junit.framework.TestCase; public class LogBuilderTest extends TestCase { public void testSerialize() { - LogBuilder builder = new LogBuilder(); + LogBuilder builder = new LogBuilder(0); builder.addTaggedData(1, "one"); builder.addTaggedData(2, "two"); Object[] out = builder.serialize(); @@ -16,7 +16,7 @@ public class LogBuilderTest extends TestCase { } public void testInvalidInputThrows() { - LogBuilder builder = new LogBuilder(); + LogBuilder builder = new LogBuilder(0); boolean threw = false; try { builder.addTaggedData(0, new Object()); @@ -28,7 +28,7 @@ public class LogBuilderTest extends TestCase { } public void testValidInputTypes() { - LogBuilder builder = new LogBuilder(); + LogBuilder builder = new LogBuilder(0); builder.addTaggedData(1, "onetwothree"); builder.addTaggedData(2, 123); builder.addTaggedData(3, 123L); diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp index ba4a4ff4fe63..76a430ee9f0e 100644 --- a/libs/androidfw/BackupData.cpp +++ b/libs/androidfw/BackupData.cpp @@ -16,14 +16,13 @@ #define LOG_TAG "backup_data" -#include <androidfw/BackupHelpers.h> -#include <utils/ByteOrder.h> - #include <stdio.h> #include <string.h> #include <unistd.h> -#include <cutils/log.h> +#include <androidfw/BackupHelpers.h> +#include <log/log.h> +#include <utils/ByteOrder.h> namespace android { diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index 78e9d91c4d67..8bfe2b6a259a 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -18,23 +18,22 @@ #include <androidfw/BackupHelpers.h> -#include <utils/KeyedVector.h> -#include <utils/ByteOrder.h> -#include <utils/String8.h> - #include <errno.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/stat.h> -#include <sys/time.h> // for utimes +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> // for utimes +#include <sys/uio.h> #include <unistd.h> #include <utime.h> -#include <fcntl.h> #include <zlib.h> -#include <cutils/log.h> +#include <log/log.h> +#include <utils/ByteOrder.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> namespace android { diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h index 8a2979a3756d..f54356576551 100644 --- a/libs/androidfw/include/androidfw/CursorWindow.h +++ b/libs/androidfw/include/androidfw/CursorWindow.h @@ -17,11 +17,11 @@ #ifndef _ANDROID__DATABASE_WINDOW_H #define _ANDROID__DATABASE_WINDOW_H -#include <cutils/log.h> #include <stddef.h> #include <stdint.h> #include <binder/Parcel.h> +#include <log/log.h> #include <utils/String8.h> #if LOG_NDEBUG diff --git a/libs/hwui/AnimatorManager.cpp b/libs/hwui/AnimatorManager.cpp index 9d5860ca0d1a..f5bb821f4e23 100644 --- a/libs/hwui/AnimatorManager.cpp +++ b/libs/hwui/AnimatorManager.cpp @@ -142,7 +142,7 @@ void AnimatorManager::animateNoDamage(TreeInfo& info) { } uint32_t AnimatorManager::animateCommon(TreeInfo& info) { - uint32_t dirtyMask; + uint32_t dirtyMask = 0; AnimateFunctor functor(info, mAnimationHandle->context(), &dirtyMask); auto newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); mAnimators.erase(newEnd, mAnimators.end()); diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 6d5833b3be86..2b4fe17d424d 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -16,7 +16,7 @@ #include "DamageAccumulator.h" -#include <cutils/log.h> +#include <log/log.h> #include "RenderNode.h" #include "utils/MathUtils.h" diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 700642ed7334..d180ba51b304 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include <DeviceInfo.h> #include "Extensions.h" -#include <GLES2/gl2.h> -#include <log/log.h> - #include <thread> #include <mutex> +#include <log/log.h> + +#include <GLES2/gl2.h> + namespace android { namespace uirenderer { diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h index f94fe2862a31..18e2330668b0 100644 --- a/libs/hwui/GpuMemoryTracker.h +++ b/libs/hwui/GpuMemoryTracker.h @@ -15,10 +15,11 @@ */ #pragma once -#include <cutils/log.h> #include <pthread.h> #include <ostream> +#include <log/log.h> + namespace android { namespace uirenderer { diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index f94a22d51d9f..d740c038f36d 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -16,10 +16,11 @@ #include "Interpolator.h" -#include "utils/MathUtils.h" - #include <algorithm> -#include <cutils/log.h> + +#include <log/log.h> + +#include "utils/MathUtils.h" namespace android { namespace uirenderer { diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index ed6b211eef1b..0a9bf5450091 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -15,19 +15,21 @@ */ #include "JankTracker.h" -#include "Properties.h" -#include "utils/TimeUtils.h" +#include <errno.h> +#include <inttypes.h> #include <algorithm> -#include <cutils/ashmem.h> -#include <cutils/log.h> +#include <cmath> #include <cstdio> -#include <errno.h> -#include <inttypes.h> #include <limits> -#include <cmath> #include <sys/mman.h> +#include <cutils/ashmem.h> +#include <log/log.h> + +#include "Properties.h" +#include "utils/TimeUtils.h" + namespace android { namespace uirenderer { diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h index 9536bc807fbc..77d5e413cb36 100644 --- a/libs/hwui/PixelBuffer.h +++ b/libs/hwui/PixelBuffer.h @@ -18,7 +18,8 @@ #define ANDROID_HWUI_PIXEL_BUFFER_H #include <GLES3/gl3.h> -#include <cutils/log.h> + +#include <log/log.h> namespace android { namespace uirenderer { diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 848161e44604..a7663817721d 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "Properties.h" +#include "Properties.h" #include "Debug.h" -#include <cutils/compiler.h> -#include <cutils/log.h> -#include <cutils/properties.h> - #include <algorithm> #include <cstdlib> +#include <log/log.h> +#include <cutils/compiler.h> +#include <cutils/properties.h> + namespace android { namespace uirenderer { diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index c5156cfd6844..f32612d21319 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -16,9 +16,11 @@ #include "SkiaCanvasProxy.h" -#include "hwui/Bitmap.h" +#include <memory> + +#include <log/log.h> -#include <cutils/log.h> +#include "hwui/Bitmap.h" #include <SkLatticeIter.h> #include <SkPatchUtils.h> #include <SkPaint.h> @@ -30,8 +32,6 @@ #include <SkSurface.h> #include <SkTextBlobRunIterator.h> -#include <memory> - namespace android { namespace uirenderer { diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index f172473d1652..956f66ed22cb 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -16,9 +16,10 @@ #include "MinikinSkia.h" +#include <log/log.h> + #include <SkPaint.h> #include <SkTypeface.h> -#include <cutils/log.h> namespace android { diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 8dd165c46d21..713e5099da26 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "MinikinUtils.h" +#include <string> + +#include <log/log.h> + #include "Paint.h" #include "SkPathMeasure.h" #include "Typeface.h" -#include <cutils/log.h> -#include <string> - namespace android { minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint, diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index 94818b2dc44f..c12747805293 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -18,7 +18,7 @@ #include "Debug.h" -#include <cutils/log.h> +#include <log/log.h> namespace android { namespace uirenderer { diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 0b22ad5ba470..7c6046789cdc 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "PointerController" - //#define LOG_NDEBUG 0 // Log debug messages about pointer updates @@ -23,8 +22,9 @@ #include "PointerController.h" -#include <cutils/log.h> +#include <log/log.h> +// ToDo: Fix code to be warning free #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkBitmap.h> diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 18ebd47558bc..4991f0434bc2 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -15,15 +15,15 @@ */ #define LOG_TAG "Sprites" - //#define LOG_NDEBUG 0 #include "SpriteController.h" -#include <cutils/log.h> +#include <log/log.h> #include <utils/String8.h> #include <gui/Surface.h> +// ToDo: Fix code to be warning free #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkBitmap.h> diff --git a/media/mca/filterpacks/native/base/geometry.cpp b/media/mca/filterpacks/native/base/geometry.cpp index 7812d5020c3f..44b13e4fff15 100644 --- a/media/mca/filterpacks/native/base/geometry.cpp +++ b/media/mca/filterpacks/native/base/geometry.cpp @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "geometry" -#include <cutils/log.h> #include <cmath> +#include <log/log.h> + #include "geometry.h" namespace android { diff --git a/media/mca/filterpacks/native/base/time_util.cpp b/media/mca/filterpacks/native/base/time_util.cpp index 1a78a953de69..7d383df5aea2 100644 --- a/media/mca/filterpacks/native/base/time_util.cpp +++ b/media/mca/filterpacks/native/base/time_util.cpp @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "time_util" #include "time_util.h" #include "utilities.h" -#include <cutils/log.h> #include <sys/time.h> #include <map> +#include <log/log.h> + namespace android { namespace filterfw { diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 4154ef02772b..329514c0b7fc 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -542,16 +542,27 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } /** - * This method is part of the SurfaceHolder.Callback interface, and is + * This method is part of the SurfaceHolder.Callback2 interface, and is * not normally called or subclassed by clients of GLSurfaceView. */ @Override - public void surfaceRedrawNeeded(SurfaceHolder holder) { + public void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable finishDrawing) { if (mGLThread != null) { - mGLThread.requestRenderAndWait(); + mGLThread.requestRenderAndNotify(finishDrawing); } } + /** + * This method is part of the SurfaceHolder.Callback2 interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + @Deprecated + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + // Since we are part of the framework we know only surfaceRedrawNeededAsync + // will be called. + } + /** * Pause the rendering thread, optionally tearing down the EGL context @@ -1305,6 +1316,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback int w = 0; int h = 0; Runnable event = null; + Runnable finishDrawingRunnable = null; while (true) { synchronized (sGLThreadManager) { @@ -1400,6 +1412,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback sGLThreadManager.notifyAll(); } + if (mFinishDrawingRunnable != null) { + finishDrawingRunnable = mFinishDrawingRunnable; + mFinishDrawingRunnable = null; + } + // Ready to draw? if (readyToDraw()) { @@ -1453,7 +1470,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback break; } } - // By design, this is the only place in a GLThread thread where we wait(). if (LOG_THREADS) { Log.i("GLThread", "waiting tid=" + getId() @@ -1546,6 +1562,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame"); view.mRenderer.onDrawFrame(gl); + if (finishDrawingRunnable != null) { + finishDrawingRunnable.run(); + finishDrawingRunnable = null; + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -1625,7 +1645,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } } - public void requestRenderAndWait() { + public void requestRenderAndNotify(Runnable finishDrawing) { synchronized(sGLThreadManager) { // If we are already on the GL thread, this means a client callback // has caused reentrancy, for example via updating the SurfaceView parameters. @@ -1638,17 +1658,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mWantRenderNotification = true; mRequestRender = true; mRenderComplete = false; + mFinishDrawingRunnable = finishDrawing; sGLThreadManager.notifyAll(); - - while (!mExited && !mPaused && !mRenderComplete && ableToDraw()) { - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } } @@ -1821,6 +1833,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private boolean mRenderComplete; private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); private boolean mSizeChanged = true; + private Runnable mFinishDrawingRunnable = null; // End of member variables protected by the sGLThreadManager monitor. diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 8cf375a47ae6..cbd295e1f85e 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -287,26 +287,23 @@ public class ExternalStorageProvider extends DocumentsProvider { String path = file.getAbsolutePath(); // Find the most-specific root path - String mostSpecificId = null; - String mostSpecificPath = null; - synchronized (mRootsLock) { - for (int i = 0; i < mRoots.size(); i++) { - final String rootId = mRoots.keyAt(i); - final String rootPath = mRoots.valueAt(i).path.getAbsolutePath(); - if (path.startsWith(rootPath) && (mostSpecificPath == null - || rootPath.length() > mostSpecificPath.length())) { - mostSpecificId = rootId; - mostSpecificPath = rootPath; - } - } + boolean visiblePath = false; + RootInfo mostSpecificRoot = getMostSpecificRootForPath(path, false); + + if (mostSpecificRoot == null) { + // Try visible path if no internal path matches. MediaStore uses visible paths. + visiblePath = true; + mostSpecificRoot = getMostSpecificRootForPath(path, true); } - if (mostSpecificPath == null) { + if (mostSpecificRoot == null) { throw new FileNotFoundException("Failed to find root that contains " + path); } // Start at first char of path under root - final String rootPath = mostSpecificPath; + final String rootPath = visiblePath + ? mostSpecificRoot.visiblePath.getAbsolutePath() + : mostSpecificRoot.path.getAbsolutePath(); if (rootPath.equals(path)) { path = ""; } else if (rootPath.endsWith("/")) { @@ -322,7 +319,29 @@ public class ExternalStorageProvider extends DocumentsProvider { } } - return mostSpecificId + ':' + path; + return mostSpecificRoot.rootId + ':' + path; + } + + private RootInfo getMostSpecificRootForPath(String path, boolean visible) { + // Find the most-specific root path + RootInfo mostSpecificRoot = null; + String mostSpecificPath = null; + synchronized (mRootsLock) { + for (int i = 0; i < mRoots.size(); i++) { + final RootInfo root = mRoots.valueAt(i); + final File rootFile = visible ? root.visiblePath : root.path; + if (rootFile != null) { + final String rootPath = rootFile.getAbsolutePath(); + if (path.startsWith(rootPath) && (mostSpecificPath == null + || rootPath.length() > mostSpecificPath.length())) { + mostSpecificRoot = root; + mostSpecificPath = rootPath; + } + } + } + } + + return mostSpecificRoot; } private File getFileForDocId(String docId) throws FileNotFoundException { @@ -519,15 +538,13 @@ public class ExternalStorageProvider extends DocumentsProvider { boolean matchesRequestedDoc = false; if (DocumentsContract.isTreeUri(uri)) { final String parentDocId = DocumentsContract.getTreeDocumentId(uri); - File parentFile = getFileForDocId(parentDocId); - if (FileUtils.contains(parentFile, doc)) { + if (isChildDocument(parentDocId, docId)) { treeUriPermission = uriPermission; matchesRequestedDoc = true; } } else { final String candidateDocId = DocumentsContract.getDocumentId(uri); - final File candidateDoc = getFileForDocId(candidateDocId); - if (Objects.equals(doc.getAbsolutePath(), candidateDoc.getAbsolutePath())) { + if (Objects.equals(docId, candidateDocId)) { docUriPermission = uriPermission; matchesRequestedDoc = true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java index de762791a0a9..320494c68faf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java @@ -41,4 +41,9 @@ public class StorageManagerVolumeProvider implements StorageVolumeProvider { public List<VolumeInfo> getVolumes() { return mStorageManager.getVolumes(); } + + @Override + public VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume) { + return mStorageManager.findEmulatedForPrivate(privateVolume); + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java index 95bb18d19188..646c42f05a8e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java @@ -34,4 +34,9 @@ public interface StorageVolumeProvider { * Returns a list of VolumeInfos for the device. */ List<VolumeInfo> getVolumes(); + + /** + * Returns the emulated volume for a given private volume. + */ + VolumeInfo findEmulatedForPrivate(VolumeInfo privateVolume); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 5574753bee6c..62f6136026df 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -483,6 +483,21 @@ android:exported="true" android:enabled="@bool/config_enableKeyguardService" /> + <activity android:name=".keyguard.WorkLockActivity" + android:label="@string/accessibility_desc_work_lock" + android:permission="android.permission.MANAGE_USERS" + android:exported="false" + android:launchMode="singleTop" + android:excludeFromRecents="true" + android:stateNotNeeded="true" + android:resumeWhilePausing="true" + android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <activity android:name=".Somnambulator" android:label="@string/start_dreams" android:icon="@mipmap/ic_launcher_dreams" diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..c3aea469d1cf --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-hdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..470b3e2c21df --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-mdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png Binary files differindex 94cb03237ac9..965d2f5f4063 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..e7137c68f752 --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..03dec1566ff9 --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..e1ca853959ca --- /dev/null +++ b/packages/SystemUI/res/drawable-ldrtl-sw900dp-xxxhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..0520c493af27 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png Binary files differnew file mode 100644 index 000000000000..3dc2b8804447 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_back_ime_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..2ba5d5b150fb --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png Binary files differnew file mode 100644 index 000000000000..62dc2d9a9eff --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_home_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png Binary files differnew file mode 100644 index 000000000000..e718b7ad1848 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_menu_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png Binary files differnew file mode 100644 index 000000000000..469800a34e12 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-hdpi/ic_sysbar_recent_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..42863c634ade --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-hdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..d9ec9d5d71b7 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-mdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png Binary files differnew file mode 100644 index 000000000000..ea7ac9361471 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..8ee4e43ab78f --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-xhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..dfa8a974f50f --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-xxhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..d23abc7f4971 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-land-xxxhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..8e56e9cf7fa4 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png Binary files differnew file mode 100644 index 000000000000..28a1465efa18 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_back_ime_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..0fc9677c0301 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png Binary files differnew file mode 100644 index 000000000000..7b81eeea67f8 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_home_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png Binary files differnew file mode 100644 index 000000000000..a6483452601e --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_menu_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png Binary files differnew file mode 100644 index 000000000000..7f7452107d94 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-mdpi/ic_sysbar_recent_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png Binary files differindex fbdc93c17ee3..3b831a3cd619 100644 --- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..8c9945518464 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png Binary files differindex 419518c099f3..3c377827f516 100644 --- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png Binary files differnew file mode 100644 index 000000000000..b2baa099aca6 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..b78dc9cb395f --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png Binary files differindex a2406b1408c1..37590a5381b6 100644 --- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png Binary files differnew file mode 100644 index 000000000000..3a208aa0ea46 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_home_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png Binary files differnew file mode 100644 index 000000000000..625ba45235b3 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_menu_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png Binary files differindex c449449541f4..7ad2a29cce6a 100644 --- a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png Binary files differnew file mode 100644 index 000000000000..604397e9728e --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xhdpi/ic_sysbar_recent_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..274e5df76fb4 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..ac1689524020 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png Binary files differnew file mode 100644 index 000000000000..e69a03790bbc --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_home_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png Binary files differnew file mode 100644 index 000000000000..65a43544732b --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_ime_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png Binary files differnew file mode 100644 index 000000000000..8bddcd9e754e --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_menu_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png Binary files differnew file mode 100644 index 000000000000..38c5959566f3 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxhdpi/ic_sysbar_recent_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png Binary files differnew file mode 100644 index 000000000000..c6d7c98a5e1e --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png Binary files differnew file mode 100644 index 000000000000..06c52aa43d71 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_back_ime_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png Binary files differnew file mode 100644 index 000000000000..766099774e52 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_docked_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png Binary files differnew file mode 100644 index 000000000000..0d9ecc2a73e2 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_home_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png Binary files differnew file mode 100644 index 000000000000..d6bb8b151f0d --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_menu_dark.png diff --git a/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png Binary files differnew file mode 100644 index 000000000000..298ef3ac7193 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw900dp-xxxhdpi/ic_sysbar_recent_dark.png diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml index ca0248e066eb..04d0e6524eac 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml @@ -27,7 +27,7 @@ <android.support.v7.widget.RecyclerView android:id="@android:id/list" - android:layout_width="@dimen/notification_panel_width" + android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollIndicators="top" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 34a0397eca8b..05963ac9ff30 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -460,6 +460,8 @@ <string name="accessibility_desc_settings">Settings</string> <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_desc_recent_apps">Overview.</string> + <!-- Content description for the graphic shown instead of an activity window while the activity is locked (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_desc_work_lock">Work lock screen</string> <!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] --> <string name="accessibility_desc_close">Close</string> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 8e5db97dd27d..ce89aab30911 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -326,6 +326,12 @@ public class KeyguardViewMediator extends SystemUI { */ private boolean mPendingLock; + /** + * Controller for showing individual "work challenge" lock screen windows inside managed profile + * tasks when the current user has been unlocked but the profile is still locked. + */ + private WorkLockActivityController mWorkLockController; + private boolean mLockLater; private boolean mWakeAndUnlocking; @@ -708,6 +714,8 @@ public class KeyguardViewMediator extends SystemUI { mHideAnimation = AnimationUtils.loadAnimation(mContext, com.android.internal.R.anim.lock_screen_behind_enter); + + mWorkLockController = new WorkLockActivityController(mContext); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java new file mode 100644 index 000000000000..12cf6fa2db20 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java @@ -0,0 +1,159 @@ +/* + * 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 android.annotation.ColorInt; +import android.annotation.UserIdInt; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.KeyguardManager; +import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Color; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.view.View; + +/** + * Bouncer between work activities and the activity used to confirm credentials before unlocking + * a managed profile. + * <p> + * Shows a solid color when started, based on the organization color of the user it is supposed to + * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if + * credentials are accepted. + */ +public class WorkLockActivity extends Activity { + private static final String TAG = "WorkLockActivity"; + + /** + * ID of the locked user that this activity blocks access to. + */ + @UserIdInt + private int mUserId; + + /** + * {@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); + + // 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)) { + 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); + + // Match task description to the task stack we are replacing so it's still recognizably the + // original task stack with the same icon and title text. + setTaskDescription(new TaskDescription(null, null, color)); + + // 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); + setContentView(blankView); + + // Respond to input events by showing the prompt to confirm credentials. + blankView.setOnClickListener((View v) -> { + showConfirmCredentialActivity(); + }); + } + + @Override + public void onDestroy() { + unregisterReceiver(mLockEventReceiver); + super.onDestroy(); + } + + @Override + public void onBackPressed() { + // Ignore back presses. + return; + } + + 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)) { + finish(); + } + } + }; + + private void showConfirmCredentialActivity() { + if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) { + // Don't show the confirm credentials screen if we are already unlocked / unlocking. + return; + } + + final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId); + if (credential == null) { + return; + } + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchTaskId(getTaskId()); + + // Bring this activity back to the foreground after confirming credentials. + final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(), + PendingIntent.FLAG_CANCEL_CURRENT | + PendingIntent.FLAG_ONE_SHOT | + PendingIntent.FLAG_IMMUTABLE, options.toBundle()); + + credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); + try { + ActivityManager.getService().startConfirmDeviceCredentialIntent(credential); + } catch (RemoteException e) { + Log.e(TAG, "Failed to start confirm credential intent", e); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java new file mode 100644 index 000000000000..22fceffd127f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -0,0 +1,61 @@ +/* + * 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 android.app.Activity; +import android.app.ActivityOptions; +import android.app.KeyguardManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; + +public class WorkLockActivityController { + private final Context mContext; + + public WorkLockActivityController(Context context) { + mContext = context; + EventBus.getDefault().register(this); + SystemServicesProxy.getInstance(context).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) + .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + | Intent.FLAG_ACTIVITY_SINGLE_TOP); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchTaskId(taskId); + options.setTaskOverlay(true); + mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); + } + + private final TaskStackListener mLockListener = new TaskStackListener() { + @Override + public void onTaskProfileLocked(int taskId, int userId) { + startWorkChallengeInTask(taskId, userId); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index d86aebfd0255..416c7cef8e5d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -47,13 +47,11 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements public FlashlightTile(Host host) { super(host); mFlashlightController = host.getFlashlightController(); - mFlashlightController.addCallback(this); } @Override protected void handleDestroy() { super.handleDestroy(); - mFlashlightController.removeCallback(this); } @Override @@ -63,6 +61,11 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements @Override public void setListening(boolean listening) { + if (listening) { + mFlashlightController.addCallback(this); + } else { + mFlashlightController.removeCallback(this); + } } @Override 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 ddffea2446a4..05df63467c02 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -154,6 +154,7 @@ public class SystemServicesProxy { public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId) { } public void onActivityDismissingDockedStack() { } + public void onTaskProfileLocked(int taskId, int userId) { } } /** @@ -197,6 +198,11 @@ public class SystemServicesProxy { public void onActivityDismissingDockedStack() throws RemoteException { mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK); } + + @Override + public void onTaskProfileLocked(int taskId, int userId) { + mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget(); + } }; /** @@ -1155,6 +1161,7 @@ public class SystemServicesProxy { private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6; + private static final int ON_TASK_PROFILE_LOCKED = 7; @Override public void handleMessage(Message msg) { @@ -1196,6 +1203,12 @@ public class SystemServicesProxy { } break; } + case ON_TASK_PROFILE_LOCKED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2); + } + break; + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index e8039c35bb4f..3059a0537b75 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -30,6 +30,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.service.vr.IVrManager; +import android.service.vr.IVrStateCallbacks; +import android.util.Log; import android.widget.ImageView; import com.android.internal.logging.MetricsLogger; @@ -52,9 +55,12 @@ public class BrightnessController implements ToggleSlider.Listener { private static final int MSG_SET_CHECKED = 2; private static final int MSG_ATTACH_LISTENER = 3; private static final int MSG_DETACH_LISTENER = 4; + private static final int MSG_VR_MODE_CHANGED = 5; private final int mMinimumBacklight; private final int mMaximumBacklight; + private final int mMinimumBacklightForVr; + private final int mMaximumBacklightForVr; private final Context mContext; private final ImageView mIcon; @@ -62,6 +68,7 @@ public class BrightnessController implements ToggleSlider.Listener { private final boolean mAutomaticAvailable; private final IPowerManager mPower; private final CurrentUserTracker mUserTracker; + private final IVrManager mVrManager; private Handler mBackgroundHandler; private final BrightnessObserver mBrightnessObserver; @@ -69,7 +76,8 @@ public class BrightnessController implements ToggleSlider.Listener { private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks = new ArrayList<BrightnessStateChangeCallback>(); - private volatile boolean mAutomatic; + private volatile boolean mAutomatic; // Brightness adjusted automatically using ambient light. + private volatile boolean mIsVrModeEnabled; private boolean mListening; private boolean mExternalChange; @@ -84,6 +92,8 @@ public class BrightnessController implements ToggleSlider.Listener { Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE); private final Uri BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + private final Uri BRIGHTNESS_FOR_VR_URI = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR); private final Uri BRIGHTNESS_ADJ_URI = Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ); @@ -105,6 +115,8 @@ public class BrightnessController implements ToggleSlider.Listener { mBackgroundHandler.post(mUpdateSliderRunnable); } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) { mBackgroundHandler.post(mUpdateSliderRunnable); + } else if (BRIGHTNESS_FOR_VR_URI.equals(uri)) { + mBackgroundHandler.post(mUpdateSliderRunnable); } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) { mBackgroundHandler.post(mUpdateSliderRunnable); } else { @@ -126,6 +138,9 @@ public class BrightnessController implements ToggleSlider.Listener { BRIGHTNESS_URI, false, this, UserHandle.USER_ALL); cr.registerContentObserver( + BRIGHTNESS_FOR_VR_URI, + false, this, UserHandle.USER_ALL); + cr.registerContentObserver( BRIGHTNESS_ADJ_URI, false, this, UserHandle.USER_ALL); } @@ -191,7 +206,14 @@ public class BrightnessController implements ToggleSlider.Listener { private final Runnable mUpdateSliderRunnable = new Runnable() { @Override public void run() { - if (mAutomatic) { + if (mIsVrModeEnabled) { + int value = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mMaximumBacklight, + UserHandle.USER_CURRENT); + mHandler.obtainMessage(MSG_UPDATE_SLIDER, + mMaximumBacklightForVr - mMinimumBacklightForVr, + value - mMinimumBacklightForVr).sendToTarget(); + } else if (mAutomatic) { float value = Settings.System.getFloatForUser(mContext.getContentResolver(), Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT); @@ -208,6 +230,14 @@ public class BrightnessController implements ToggleSlider.Listener { } }; + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + @Override + public void onVrStateChanged(boolean enabled) { + mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0) + .sendToTarget(); + } + }; + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -230,6 +260,9 @@ public class BrightnessController implements ToggleSlider.Listener { case MSG_DETACH_LISTENER: mControl.setOnChangedListener(null); break; + case MSG_VR_MODE_CHANGED: + updateVrMode(msg.arg1 != 0); + break; default: super.handleMessage(msg); } @@ -256,10 +289,13 @@ public class BrightnessController implements ToggleSlider.Listener { PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); mMaximumBacklight = pm.getMaximumScreenBrightnessSetting(); + mMinimumBacklightForVr = pm.getMinimumScreenBrightnessForVrSetting(); + mMaximumBacklightForVr = pm.getMaximumScreenBrightnessForVrSetting(); mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power")); + mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager")); } public void setBackgroundLooper(Looper backgroundLooper) { @@ -284,6 +320,15 @@ public class BrightnessController implements ToggleSlider.Listener { return; } + if (mVrManager != null) { + try { + mVrManager.registerListener(mVrStateCallbacks); + mIsVrModeEnabled = mVrManager.getVrModeState(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register VR mode state listener: ", e); + } + } + mBackgroundHandler.post(mStartListeningRunnable); mListening = true; } @@ -294,6 +339,14 @@ public class BrightnessController implements ToggleSlider.Listener { return; } + if (mVrManager != null) { + try { + mVrManager.unregisterListener(mVrStateCallbacks); + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister VR mode state listener: ", e); + } + } + mBackgroundHandler.post(mStopListeningRunnable); mListening = false; } @@ -304,7 +357,22 @@ public class BrightnessController implements ToggleSlider.Listener { updateIcon(mAutomatic); if (mExternalChange) return; - if (!mAutomatic) { + if (mIsVrModeEnabled) { + final int val = value + mMinimumBacklightForVr; + if (stopTracking) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS_FOR_VR, val); + } + setBrightness(val); + if (!tracking) { + AsyncTask.execute(new Runnable() { + public void run() { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FOR_VR, val, + UserHandle.USER_CURRENT); + } + }); + } + } else if (!mAutomatic) { final int val = value + mMinimumBacklight; if (stopTracking) { MetricsLogger.action(mContext, MetricsEvent.ACTION_BRIGHTNESS, val); @@ -368,4 +436,11 @@ public class BrightnessController implements ToggleSlider.Listener { com.android.systemui.R.drawable.ic_qs_brightness_auto_off); } } + + private void updateVrMode(boolean isEnabled) { + if (mIsVrModeEnabled != isEnabled) { + mIsVrModeEnabled = isEnabled; + mBackgroundHandler.post(mUpdateSliderRunnable); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java index 008d837da51c..f0cfa2c40931 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java @@ -121,6 +121,8 @@ public class FlashlightControllerImpl implements FlashlightController { } cleanUpListenersLocked(l); mListeners.add(new WeakReference<>(l)); + l.onFlashlightAvailabilityChanged(mTorchAvailable); + l.onFlashlightChanged(mFlashlightEnabled); } } diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto index cf372bc90c74..b5afc402b7ab 100644 --- a/proto/src/ipconnectivity.proto +++ b/proto/src/ipconnectivity.proto @@ -17,14 +17,26 @@ message NetworkId { optional int32 network_id = 1; }; -// Transport describes a physical technology used by a network. It is a subset -// of the TRANSPORT_* constants defined in android.net.NetworkCapabilities. -enum Transport { +// LinkLayer describes a physical link layer technology used by a network. +// It is not intended to map one to one to the TRANSPORT_* constants defined in +// android.net.NetworkCapabilities. Instead it is intended to be used as +// a dimension field for metrics events and aggregated metrics. +// Next tag: 7 +enum LinkLayer { + // An unknown link layer technology. UNKNOWN = 0; + BLUETOOTH = 1; CELLULAR = 2; ETHERNET = 3; WIFI = 4; + + // Indicates that the link layer dimension is not relevant for the metrics or + // event considered. + NONE = 5; + + // Indicates that the metrics or event considered may involve several links. + MULTIPLE = 6; }; // A pair of (key, value) integers for describing histogram-like statistics. @@ -65,7 +77,7 @@ message DefaultNetworkEvent { // This message is associated to android.net.metrics.IpReachabilityEvent. message IpReachabilityEvent { // The interface name (wlan, rmnet, lo, ...) on which the probe was sent. - // Deprecated since version 2, replaced by transport field. + // Deprecated since version 2, to be replaced by link_layer field. optional string if_name = 1 [deprecated = true]; // The event type code of the probe, represented by constants defined in @@ -179,7 +191,7 @@ message ConnectStatistics { // android.net.metrics.DhcpErrorEvent. message DHCPEvent { // The interface name (wlan, rmnet, lo, ...) on which the event happened. - // Deprecated since version 2, replaced by transport field. + // Deprecated since version 2, to be replaced by link_layer field. optional string if_name = 1 [deprecated = true]; oneof value { @@ -284,7 +296,7 @@ message RaEvent { // This message is associated to android.net.metrics.IpManagerEvent. message IpProvisioningEvent { // The interface name (wlan, rmnet, lo, ...) on which the probe was sent. - // Deprecated since version 2, replaced by transport field. + // Deprecated since version 2, to be replaced by link_layer field. optional string if_name = 1 [deprecated = true]; // The code of the IP provisioning event, represented by constants defined in @@ -296,19 +308,21 @@ message IpProvisioningEvent { } // Represents one of the IP connectivity event defined in this file. -// Next tag: 15 +// Next tag: 16 message IpConnectivityEvent { // Time in ms when the event was recorded. optional int64 time_ms = 1; - // Physical transport of the network on which the event happened. + // Physical link layer of the network on which the event happened. + // Acts as a dimension key. // Since version 2. - optional Transport transport = 12; + optional LinkLayer link_layer = 15; // Event type. oneof event { // An event about the system default network. + // The link_layer field is not relevant for this event and set to NONE. DefaultNetworkEvent default_network_event = 2; // An IP reachability probe event. @@ -318,7 +332,8 @@ message IpConnectivityEvent { NetworkEvent network_event = 4; // A batch of DNS lookups. - // Deprecated in the nyc-mr2 release since version 2, and replaced by dns_latencies. + // Deprecated in the nyc-mr2 release since version 2,and replaced by + // dns_latencies. DNSLookupBatch dns_lookup_batch = 5 [deprecated = true]; // DNS lookup latency statistics. diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 57c45654da9d..21139da7900f 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3105,6 +3105,12 @@ message MetricsEvent { // PACKAGE: The package name of the app the permission was revoked for ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBER = 739; + // ACTION: QS Brightness Slider (with auto brightness disabled, and VR enabled) + // SUBTYPE: slider value + // CATEGORY: QUICK_SETTINGS + // OS: 6.0 + ACTION_BRIGHTNESS_FOR_VR = 498; + // ACTION: A captive portal was detected during network validation // CATEGORY: NOTIFICATION // OS: N-MR2 @@ -3175,6 +3181,11 @@ message MetricsEvent { // user accepted ACTION_SKIP_DISCLAIMER_SELECTED = 760; + // Enclosing category for group of APP_TRANSITION_FOO events, + // logged when we execute an app transition. + APP_TRANSITION = 761; + + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index 6a1613113c10..87eaf29fb306 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -18,28 +18,23 @@ package com.android.server.autofill; import static android.Manifest.permission.MANAGE_AUTO_FILL; import static android.content.Context.AUTO_FILL_MANAGER_SERVICE; -import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT; -import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT; +import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL; +import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; + +import static com.android.server.autofill.AutoFillUI.MSG_SHOW_ALL_NOTIFICATIONS; +import static com.android.server.autofill.AutoFillUI.SHOW_ALL_NOTIFICATIONS_DELAY_MS; import android.Manifest; import android.app.AppGlobals; -import android.app.Notification; -import android.app.Notification.Action; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -48,7 +43,6 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManager; import android.provider.Settings; import android.service.autofill.IAutoFillManagerService; import android.text.TextUtils; @@ -65,7 +59,6 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.List; /** * Entry point service for auto-fill management. @@ -86,6 +79,7 @@ public final class AutoFillManagerService extends SystemService { protected static final int MSG_UNBIND = 1; private final AutoFillManagerServiceStub mServiceStub; + private final AutoFillUI mUi; private final Context mContext; private final ContentResolver mResolver; @@ -99,7 +93,7 @@ public final class AutoFillManagerService extends SystemService { removeStaleServiceForUser(msg.arg1); return; case MSG_SHOW_ALL_NOTIFICATIONS: - showAllNotifications(); + mUi.showAllNotifications(); return; default: Slog.w(TAG, "Invalid message: " + msg); @@ -129,6 +123,7 @@ public final class AutoFillManagerService extends SystemService { super(context); mContext = context; + mUi = new AutoFillUI(context, this, mLock); mResolver = context.getContentResolver(); mServiceStub = new AutoFillManagerServiceStub(); } @@ -176,8 +171,9 @@ public final class AutoFillManagerService extends SystemService { Slog.w(TAG, "no service info for " + serviceComponent); return null; } - return new AutoFillManagerServiceImpl(this, mContext, mLock, FgThread.getHandler(), userId, - serviceInfo.applicationInfo.uid, serviceComponent, SERVICE_BINDING_LIFETIME_MS); + return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, FgThread.getHandler(), + userId, serviceInfo.applicationInfo.uid, serviceComponent, + SERVICE_BINDING_LIFETIME_MS); } /** @@ -186,7 +182,8 @@ public final class AutoFillManagerService extends SystemService { * <p>First it tries to return the existing instance from the cache; if it's not cached, it * creates a new instance and caches it. */ - private AutoFillManagerServiceImpl getServiceForUserLocked(int userId) { + // TODO(b/33197203): make private once AutoFillUi does not uses notifications + AutoFillManagerServiceImpl getServiceForUserLocked(int userId) { AutoFillManagerServiceImpl service = mServicesCache.get(userId); if (service != null) { if (DEBUG) Log.d(TAG, "reusing cached service for userId " + userId); @@ -251,14 +248,14 @@ public final class AutoFillManagerService extends SystemService { final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub { @Override - public void requestAutoFill(IBinder activityToken, int userId, int flags) { + public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) { if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId); mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); synchronized (mLock) { final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null) { - service.requestAutoFill(activityToken, flags); + service.requestAutoFill(activityToken, extras, flags); } } } @@ -310,147 +307,8 @@ public final class AutoFillManagerService extends SystemService { if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId); synchronized (mLock) { removeCachedServiceForUserLocked(userId); - final ComponentName serviceComponent = getProviderForUser(userId); - if (serviceComponent == null) { - cancelNotificationLocked(userId); - } else { - showNotification(serviceComponent, userId); - } - } - } - } - - //////////////////////////////////////////////////////////////////////////// - // TODO: 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: remove from frameworks/base/core/res/AndroidManifest.xml once it's not used anymore - private static final String NOTIFICATION_AUTO_FILL_INTENT = - "com.android.internal.autofill.action.REQUEST_AUTOFILL"; - private static final String EXTRA_USER_ID = "user_id"; - private static final String EXTRA_FLAGS = "flags"; - - private static final int MSG_SHOW_ALL_NOTIFICATIONS = 42; - private static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000; - - private BroadcastReceiver mNotificationReceiver; - - final class NotificationReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(EXTRA_USER_ID, -1); - final int flags = intent.getIntExtra(EXTRA_FLAGS, 0); - if (DEBUG) Slog.d(TAG, "Requesting autofill by notification for user " + userId); - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); - if (service == null) { - Slog.w(TAG, "no auto-fill service for user " + userId); - } else { - service.requestAutoFill(null, flags); - } - } - } - } - - private ComponentName getProviderForUser(int userId) { - ComponentName serviceComponent = null; - ServiceInfo serviceInfo = null; - final String componentName = Settings.Secure.getStringForUser( - mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId); - if (!TextUtils.isEmpty(componentName)) { - try { - serviceComponent = ComponentName.unflattenFromString(componentName); - serviceInfo = - AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId); - } catch (RuntimeException | RemoteException e) { - Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e); - return null; + mUi.updateNotification(userId); } } - - if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component=" - + serviceComponent + ", info: " + serviceInfo); - if (serviceInfo == null) { - Slog.w(TAG, "no service info for " + serviceComponent); - return null; - } - return serviceComponent; } - - private void showAllNotifications() { - final UserManager userManager = - (UserManager) mContext.getSystemService(Context.USER_SERVICE); - - final List<UserInfo> allUsers = userManager.getUsers(true); - - for (UserInfo user : allUsers) { - final ComponentName serviceComponent = getProviderForUser(user.id); - if (serviceComponent != null) { - showNotification(serviceComponent, user.id); - } - } - } - - private void showNotification(ComponentName serviceComponent, int userId) { - if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent); - - synchronized (mLock) { - if (mNotificationReceiver == null) { - mNotificationReceiver = new NotificationReceiver(); - mContext.registerReceiver(mNotificationReceiver, - new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT)); - } - } - - final Intent fillIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT); - fillIntent.putExtra(EXTRA_USER_ID, userId); - fillIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_SANITIZED_TEXT); - final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext, - ASSIST_FLAG_SANITIZED_TEXT, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT); - final Action fillAction = new Action.Builder(null, "FILL", fillPendingIntent).build(); - - final Intent saveIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT); - saveIntent.putExtra(EXTRA_USER_ID, userId); - saveIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_NON_SANITIZED_TEXT); - final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext, - ASSIST_FLAG_NON_SANITIZED_TEXT, saveIntent, PendingIntent.FLAG_UPDATE_CURRENT); - final Action saveAction = new Action.Builder(null, "SAVE", savePendingIntent).build(); - - final String packageName = serviceComponent.getPackageName(); - String providerName = null; - final PackageManager pm = mContext.getPackageManager(); - try { - final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId); - if (info != null) { - providerName = pm.getApplicationLabel(info).toString(); - } - } catch (Exception e) { - providerName = packageName; - } - final String title = "AutoFill actions"; - final String subTitle = "Provider: " + providerName + "\n" + "User: " + userId; - - final Notification notification = new Notification.Builder(mContext) - .setCategory(Notification.CATEGORY_SYSTEM) - .setOngoing(true) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setLocalOnly(true) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(title) - .setStyle(new Notification.BigTextStyle().bigText(subTitle)) - .setActions(fillAction, saveAction) - .build(); - NotificationManager.from(mContext).notify(userId, notification); - } - - private void cancelNotificationLocked(int userId) { - if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId); - NotificationManager.from(mContext).cancel(userId); - } - - ///////////////////////////////////////// - // End of temporary notification code. // - ///////////////////////////////////////// } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index 82356c8458b3..3de8a8bac511 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -18,9 +18,12 @@ package com.android.server.autofill; import static com.android.server.autofill.AutoFillManagerService.DEBUG; +import android.annotation.Nullable; +import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.IActivityManager; +import android.app.assist.AssistStructure; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -29,6 +32,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.icu.text.DateFormat; +import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; @@ -37,16 +41,24 @@ import android.os.SystemClock; import android.os.UserHandle; import android.service.autofill.AutoFillService; import android.service.autofill.AutoFillServiceInfo; +import android.service.autofill.IAutoFillAppCallback; +import android.service.autofill.IAutoFillServerCallback; import android.service.autofill.IAutoFillService; -import android.util.Log; +import android.service.voice.VoiceInteractionSession; import android.util.PrintWriterPrinter; import android.util.Slog; +import android.util.SparseArray; import android.util.TimeUtils; +import android.view.autofill.AutoFillId; +import android.view.autofill.Dataset; +import android.view.autofill.FillResponse; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; import com.android.server.LocalServices; import java.io.PrintWriter; +import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -60,6 +72,9 @@ final class AutoFillManagerServiceImpl { private static final String TAG = "AutoFillManagerServiceImpl"; + /** Used do assign ids to new ServerCallback instances. */ + private static int sServerCallbackCounter = 0; + private final int mUserId; private final int mUid; private final ComponentName mComponent; @@ -68,6 +83,7 @@ final class AutoFillManagerServiceImpl { private final Object mLock; private final AutoFillServiceInfo mInfo; private final AutoFillManagerService mManagerService; + private final AutoFillUI mUi; // TODO(b/33197203): improve its usage // - set maximum number of entries @@ -89,10 +105,19 @@ final class AutoFillManagerServiceImpl { } }; + /** + * Cache of pending ServerCallbacks, keyed by {@link ServerCallback#id}. + * + * <p>They're kept until the AutoFillService handles a request, or an error occurs. + */ + // TODO(b/33197203): need to make sure service is bound while callback is pending + @GuardedBy("mLock") + private static final SparseArray<ServerCallback> mServerCallbacks = new SparseArray<>(); + private final ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Log.d(TAG, "onServiceConnected():" + name); + if (DEBUG) Slog.d(TAG, "onServiceConnected():" + name); synchronized (mLock) { mService = IAutoFillService.Stub.asInterface(service); try { @@ -102,17 +127,18 @@ final class AutoFillManagerServiceImpl { return; } if (!mQueuedRequests.isEmpty()) { - if (DEBUG) Log.d(TAG, "queued requests:" + mQueuedRequests.size()); + if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size()); } for (final QueuedRequest request: mQueuedRequests) { - requestAutoFillLocked(request.activityToken, request.flags, false); + requestAutoFillLocked(request.activityToken, request.extras, request.flags, + false); } } } @Override public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Log.d(TAG, name + " disconnected"); + if (DEBUG) Slog.d(TAG, name + " disconnected"); synchronized (mLock) { mService = null; mManagerService.removeCachedServiceForUserLocked(mUserId); @@ -120,6 +146,39 @@ final class AutoFillManagerServiceImpl { } }; + + /** + * Receiver of assist data from the app's {@link Activity}, uses the {@code resultData} as + * the {@link ServerCallback#id}. + */ + private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + if (DEBUG) Slog.d(TAG, "resultCode on mAssistReceiver: " + resultCode); + + final IBinder appBinder = resultData.getBinder(AutoFillService.KEY_CALLBACK); + if (appBinder == null) { + Slog.w(TAG, "no app callback on mAssistReceiver's resultData"); + return; + } + final AssistStructure structure = resultData + .getParcelable(VoiceInteractionSession.KEY_STRUCTURE); + final Bundle data = resultData.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS); + final int flags = resultData.getInt(VoiceInteractionSession.KEY_FLAGS, 0); + + final ServerCallback serverCallback; + synchronized (mLock) { + serverCallback = mServerCallbacks.get(resultCode); + if (serverCallback == null) { + Slog.w(TAG, "no server callback for id " + resultCode); + return; + } + serverCallback.appCallback = IAutoFillAppCallback.Stub.asInterface(appBinder); + } + mService.autoFill(structure, serverCallback, serverCallback.extras, flags); + } + }; + @GuardedBy("mLock") private IAutoFillService mService; private boolean mBound; @@ -128,9 +187,11 @@ final class AutoFillManagerServiceImpl { // Estimated time when the service will be evicted from the cache. long mEstimateTimeOfDeath; - AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock, - Handler handler, int userId, int uid,ComponentName component, long ttl) { + AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui, + Context context, Object lock, Handler handler, int userId, int uid, + ComponentName component, long ttl) { mManagerService = managerService; + mUi = ui; mContext = context; mLock = lock; mUserId = userId; @@ -180,7 +241,14 @@ final class AutoFillManagerServiceImpl { if (DEBUG) Slog.d(TAG, "Bound to " + mComponent); } - void requestAutoFill(IBinder activityToken, int flags) { + /** + * Asks service to auto-fill an activity. + * + * @param activityToken activity token + * @param extras bundle to be passed to the {@link AutoFillService} method. + * @param flags optional flags. + */ + void requestAutoFill(@Nullable IBinder activityToken, @Nullable Bundle extras, int flags) { synchronized (mLock) { if (!mBound) { Slog.w(TAG, "requestAutoFill() failed because it's not bound to service"); @@ -211,21 +279,26 @@ final class AutoFillManagerServiceImpl { DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken; synchronized (mLock) { mRequestHistory.add(historyItem); - requestAutoFillLocked(activityToken, flags, true); + requestAutoFillLocked(activityToken, extras, flags, true); } } - private void requestAutoFillLocked(IBinder activityToken, int flags, boolean queueIfNecessary) { + private void requestAutoFillLocked(IBinder activityToken, @Nullable Bundle extras, int flags, + boolean queueIfNecessary) { if (mService == null) { if (!queueIfNecessary) { Slog.w(TAG, "requestAutoFillLocked(): service is null"); return; } if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it"); - mQueuedRequests.add(new QueuedRequest(activityToken, flags)); + mQueuedRequests.add(new QueuedRequest(activityToken, extras, flags)); return; } + final int callbackId = ++sServerCallbackCounter; + final ServerCallback serverCallback = new ServerCallback(callbackId, extras); + mServerCallbacks.put(callbackId, serverCallback); + /* * TODO(b/33197203): apply security checks below: * - checks if disabled by secure settings / device policy @@ -235,8 +308,7 @@ final class AutoFillManagerServiceImpl { */ try { // TODO(b/33197203): add MetricsLogger call - if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken, - flags)) { + if (!mAm.requestAutoFillData(mAssistReceiver, null, callbackId, activityToken, flags)) { // TODO(b/33197203): might need a way to warn user (perhaps a new method on // AutoFillService). Slog.w(TAG, "failed to request auto-fill data for " + activityToken); @@ -251,7 +323,7 @@ final class AutoFillManagerServiceImpl { // Sanity check. if (mService == null) { - Log.w(TAG, "service already null on shutdown"); + Slog.w(TAG, "service already null on shutdown"); return; } try { @@ -273,6 +345,44 @@ final class AutoFillManagerServiceImpl { } } + /** + * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset. + */ + void autoFillApp(int callbackId, Dataset dataset) { + // TODO(b/33197203): add MetricsLogger call + + if (dataset == null) { + Slog.w(TAG, "autoFillApp(): no dataset for callback id " + callbackId); + return; + } + + final ServerCallback serverCallback; + synchronized (mLock) { + serverCallback = mServerCallbacks.get(callbackId); + if (serverCallback == null) { + Slog.w(TAG, "autoFillApp(): no server callback with id " + callbackId); + return; + } + if (serverCallback.appCallback == null) { + Slog.w(TAG, "autoFillApp(): no app callback for server callback " + callbackId); + return; + } + // TODO(b/33197203): use a handler? + try { + if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); + serverCallback.appCallback.autoFill(dataset); + } catch (RemoteException e) { + Slog.w(TAG, "Error auto-filling activity: " + e); + } + removeServerCallbackLocked(callbackId); + } + } + + void removeServerCallbackLocked(int id) { + if (DEBUG) Slog.d(TAG, "Removing " + id + " from server callbacks"); + mServerCallbacks.remove(id); + } + void dumpLocked(String prefix, PrintWriter pw) { if (!mValid) { pw.print(" NOT VALID: "); @@ -284,6 +394,8 @@ final class AutoFillManagerServiceImpl { return; } + final String prefix2 = prefix + " "; + pw.print(prefix); pw.print("mUserId="); pw.println(mUserId); pw.print(prefix); pw.print("mUid="); pw.println(mUid); pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString()); @@ -303,7 +415,6 @@ final class AutoFillManagerServiceImpl { pw.print(prefix); pw.println("No history"); } else { pw.print(prefix); pw.println("History:"); - final String prefix2 = prefix + prefix; for (int i = 0; i < mRequestHistory.size(); i++) { pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i)); } @@ -312,11 +423,28 @@ final class AutoFillManagerServiceImpl { pw.print(prefix); pw.println("No queued requests"); } else { pw.print(prefix); pw.println("Queued requests:"); - final String prefix2 = prefix + prefix; for (int i = 0; i < mQueuedRequests.size(); i++) { pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mQueuedRequests.get(i)); } } + + pw.print(prefix); pw.print("sServerCallbackCounter="); pw.println(sServerCallbackCounter); + final int size = mServerCallbacks.size(); + if (size == 0) { + pw.print(prefix); pw.println("No server callbacks"); + } else { + pw.print(prefix); pw.print(size); pw.println(" server callbacks:"); + for (int i = 0; i < size; i++) { + pw.print(prefix2); pw.print(mServerCallbacks.keyAt(i)); + final ServerCallback callback = mServerCallbacks.valueAt(i); + if (callback.appCallback == null) { + pw.println("(no appCallback)"); + } else { + pw.print(" (app callback: "); pw.print(callback.appCallback) ; pw.println(")"); + } + } + pw.println(); + } } @Override @@ -327,10 +455,12 @@ final class AutoFillManagerServiceImpl { private static final class QueuedRequest { final IBinder activityToken; + final Bundle extras; final int flags; - QueuedRequest(IBinder activityToken, int flags) { + QueuedRequest(IBinder activityToken, Bundle extras, int flags) { this.activityToken = activityToken; + this.extras = extras; this.flags = flags; } @@ -339,4 +469,54 @@ final class AutoFillManagerServiceImpl { return "flags: " + flags + " token: " + activityToken; } } + + /** + * A bridge between the {@link AutoFillService} implementation and the activity being + * auto-filled (represented through the {@link IAutoFillAppCallback}). + */ + private final class ServerCallback extends IAutoFillServerCallback.Stub { + + private final int id; + private final Bundle extras; + private IAutoFillAppCallback appCallback; + + private ServerCallback(int id, Bundle extras) { + this.id = id; + this.extras = extras; + } + + @Override + public void showResponse(FillResponse response) { + // TODO(b/33197203): add MetricsLogger call + if (DEBUG) Slog.d(TAG, "showResponse(): " + response); + + mUi.showOptions(mUserId, id, response); + } + + @Override + public void showError(String message) { + // TODO(b/33197203): add MetricsLogger call + if (DEBUG) Slog.d(TAG, "showError(): " + message); + + mUi.showError(message); + + removeSelf(); + } + + @Override + public void highlightSavedFields(AutoFillId[] ids) { + // TODO(b/33197203): add MetricsLogger call + if (DEBUG) Slog.d(TAG, "showSaved(): " + Arrays.toString(ids)); + + mUi.highlightSavedFields(ids); + + removeSelf(); + } + + private void removeSelf() { + synchronized (mLock) { + removeServerCallbackLocked(id); + } + } + } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java index aa3503b9a6d3..26f24519ffc8 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java @@ -16,10 +16,11 @@ package com.android.server.autofill; -import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT; -import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT; +import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL; +import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; import android.app.ActivityManager; +import android.os.Bundle; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; @@ -44,9 +45,9 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand { try { switch (cmd) { case "fill": - return requestAutoFill(ASSIST_FLAG_SANITIZED_TEXT); + return requestAutoFill(AUTO_FILL_FLAG_TYPE_FILL); case "save": - return requestAutoFill(ASSIST_FLAG_NON_SANITIZED_TEXT); + return requestAutoFill(AUTO_FILL_FLAG_TYPE_SAVE); default: return handleDefaultCommands(cmd); } @@ -73,7 +74,7 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand { private int requestAutoFill(int flags) throws RemoteException { final int userId = getUserIdFromArgs(); - mService.requestAutoFill(null, userId, flags); + mService.requestAutoFill(null, userId, null, flags); return 0; } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java new file mode 100644 index 000000000000..08e81d36f9cf --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2016 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 static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; +import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL; + +import static com.android.server.autofill.AutoFillManagerService.DEBUG; + +import android.app.Activity; +import android.app.AppGlobals; +import android.app.Notification; +import android.app.Notification.Action; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.os.Binder; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.UserManager; +import android.provider.Settings; +import android.service.autofill.AutoFillService; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.view.autofill.Dataset; +import android.view.autofill.AutoFillId; +import android.view.autofill.FillResponse; +import android.widget.Toast; + +import com.android.server.UiThread; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Handles all auto-fill related UI tasks. + */ +// TODO(b/33197203): document exactly what once the auto-fill bar is implemented +final class AutoFillUI { + + private static final String TAG = "AutoFillUI"; + + private final Context mContext; + + AutoFillUI(Context context, AutoFillManagerService service, Object lock) { + mContext = context; + mResolver = context.getContentResolver(); + mService = service; + mLock = lock; + } + + /** + * Displays an error message to the user. + */ + void showError(String message) { + // TODO(b/33197203): proper implementation + UiThread.getHandler().runWithScissors(() -> { + Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show(); + }, 0); + } + + /** + * Highlights in the {@link Activity} the fields saved by the service. + */ + void highlightSavedFields(AutoFillId[] ids) { + // TODO(b/33197203): proper implementation (must be handled by activity) + UiThread.getHandler().runWithScissors(() -> { + Toast.makeText(mContext, "AutoFill: service saved ids " + Arrays.toString(ids), + Toast.LENGTH_LONG).show(); + }, 0); + } + + /** + * Shows the options from a {@link FillResponse} so the user can pick up the proper + * {@link Dataset} (when the response has one). + */ + void showOptions(int userId, int callbackId, FillResponse response) { + // TODO(b/33197203): proper implementation + // TODO(b/33197203): make sure if removes the callback from cache + showOptionsNotification(userId, callbackId, response); + } + + ///////////////////////////////////////////////////////////////////////////////// + // 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"; + + // Extras used in the notification intents + private static final String EXTRA_USER_ID = "user_id"; + private static final String EXTRA_NOTIFICATION_TYPE = "notification_type"; + private static final String EXTRA_CALLBACK_ID = "callback_id"; + private static final String EXTRA_FILL_RESPONSE = "fill_response"; + private static final String EXTRA_DATASET = "dataset"; + + private static final String TYPE_EMULATE = "emulate"; + private static final String TYPE_OPTIONS = "options"; + private static final String TYPE_DELETE_CALLBACK = "delete_callback"; + private static final String TYPE_PICK_DATASET = "pick_dataset"; + private static final String TYPE_SAVE = "save"; + + static final int MSG_SHOW_ALL_NOTIFICATIONS = 42; + static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000; + + private BroadcastReceiver mNotificationReceiver; + private final ContentResolver mResolver; + private final AutoFillManagerService mService; + private final Object mLock; + + // Hack used to generate unique pending intents + static int sResultCode = 0; + + final class NotificationReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(EXTRA_USER_ID, -1); + + final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId); + if (service == null) { + Slog.w(TAG, "no auto-fill service for user " + userId); + return; + } + + final int callbackId = intent.getIntExtra(EXTRA_CALLBACK_ID, -1); + final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE); + if (type == null) { + Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent); + return; + } + final FillResponse fillData = intent.getParcelableExtra(EXTRA_FILL_RESPONSE); + final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET); + final Bundle datasetArgs = dataset == null ? null : dataset.getExtras(); + final Bundle fillDataArgs = fillData == null ? null : fillData.getExtras(); + + // Bundle sent on AutoFillService methods - only set if service provided a bundle + final Bundle extras = (datasetArgs == null && fillDataArgs == null) + ? null : new Bundle(); + + if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId + + ", callbackId=" + callbackId); + synchronized (mLock) { + switch (type) { + case TYPE_EMULATE: + service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_FILL); + break; + case TYPE_SAVE: + if (datasetArgs != null) { + if (DEBUG) Log.d(TAG, "filldata args on save notificataion: " + + bundleToString(fillDataArgs)); + extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, fillDataArgs); + } + if (dataset != null) { + if (DEBUG) Log.d(TAG, "dataset args on save notificataion: " + + bundleToString(datasetArgs)); + extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs); + } + service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE); + break; + case TYPE_DELETE_CALLBACK: + service.removeServerCallbackLocked(callbackId); + break; + case TYPE_PICK_DATASET: + service.autoFillApp(callbackId, dataset); + // Must cancel notification because it might be comming from action + if (DEBUG) Log.d(TAG, "Cancelling notification"); + NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId); + + if (datasetArgs != null) { + if (DEBUG) Log.d(TAG, "adding dataset's extra_data on save intent: " + + bundleToString(datasetArgs)); + extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs); + } + + // Also show notification with option to save the data + showSaveNotification(userId, fillData, dataset); + break; + default: { + Slog.w(TAG, "Unknown notification type: " + type); + } + } + } + } + } + + private ComponentName getProviderForUser(int userId) { + ComponentName serviceComponent = null; + ServiceInfo serviceInfo = null; + final String componentName = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId); + if (!TextUtils.isEmpty(componentName)) { + try { + serviceComponent = ComponentName.unflattenFromString(componentName); + serviceInfo = + AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId); + } catch (RuntimeException | RemoteException e) { + Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e); + return null; + } + } + + if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component=" + + serviceComponent + ", info: " + serviceInfo); + if (serviceInfo == null) { + Slog.w(TAG, "no service info for " + serviceComponent); + return null; + } + return serviceComponent; + } + + void showAllNotifications() { + final UserManager userManager = + (UserManager) mContext.getSystemService(Context.USER_SERVICE); + + final List<UserInfo> allUsers = userManager.getUsers(true); + + for (UserInfo user : allUsers) { + final ComponentName serviceComponent = getProviderForUser(user.id); + if (serviceComponent != null) { + showMainNotification(serviceComponent, user.id); + } + } + } + + void updateNotification(int userId) { + final ComponentName serviceComponent = getProviderForUser(userId); + if (serviceComponent == null) { + cancelMainNotification(userId); + } else { + showMainNotification(serviceComponent, userId); + } + } + + private static Intent newNotificationIntent(int userId, String type) { + final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT); + intent.putExtra(EXTRA_USER_ID, userId); + intent.putExtra(EXTRA_NOTIFICATION_TYPE, type); + return intent; + } + + private PendingIntent newPickDatasetPI(int userId, int callbackId, FillResponse response, + Dataset dataset) { + final int resultCode = ++ sResultCode; + if (DEBUG) Log.d(TAG, "newPickDatasetPI: userId=" + userId + ", callback=" + callbackId + + ", resultCode=" + resultCode); + + final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET); + intent.putExtra(EXTRA_CALLBACK_ID, callbackId); + intent.putExtra(EXTRA_FILL_RESPONSE, response); + intent.putExtra(EXTRA_DATASET, dataset); + return PendingIntent.getBroadcast(mContext, resultCode, intent, + PendingIntent.FLAG_ONE_SHOT); + } + + private static String bundleToString(Bundle bundle) { + if (bundle == null) { + return "null"; + } + final Set<String> keySet = bundle.keySet(); + final StringBuilder builder = new StringBuilder("[Bundle with ").append(keySet.size()) + .append(" keys:"); + for (String key : keySet) { + final Object value = bundle.get(key); + builder.append(' ').append(key).append('='); + builder.append((value instanceof Object[]) + ? Arrays.toString((Objects[]) value) : value); + } + return builder.append(']').toString(); + } + + /** + * Shows a permanent notification that triggers the auto-fill workflow for the given user. + * + * <p>It emulates calling the auto-fill service when the IME is shown. + */ + private void showMainNotification(ComponentName serviceComponent, int userId) { + if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent); + + synchronized (mLock) { + if (mNotificationReceiver == null) { + mNotificationReceiver = new NotificationReceiver(); + mContext.registerReceiver(mNotificationReceiver, + new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT)); + } + } + + final Intent fillIntent = newNotificationIntent(userId, TYPE_EMULATE); + final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext, + -1, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + final String packageName = serviceComponent.getPackageName(); + String providerName = null; + final PackageManager pm = mContext.getPackageManager(); + try { + final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId); + if (info != null) { + providerName = pm.getApplicationLabel(info).toString(); + } + } catch (Exception e) { + providerName = packageName; + } + final String title = "AutoFill IME Emulation"; + final String subTitle = "Tap notification to start auto-fill workflow (by '" + providerName + + "' on top activity on user " + userId + ".\n" + + "Once provider replies, a new notification will show your options."; + + final Notification notification = new Notification.Builder(mContext) + .setCategory(Notification.CATEGORY_SYSTEM) + .setOngoing(true) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setLocalOnly(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setStyle(new Notification.BigTextStyle().bigText(subTitle)) + .setContentIntent(fillPendingIntent) + .build(); + NotificationManager.from(mContext).notify(TYPE_EMULATE, userId, notification); + } + + /** + * Cancels the permament notification created by + * {@link #showMainNotification(ComponentName, int)}. + */ + private void cancelMainNotification(int userId) { + if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId); + NotificationManager.from(mContext).cancel(TYPE_EMULATE, userId); + } + + /** + * Shows a notification with the results of an auto-fill request, using notications actions + * to emulate the auto-fill bar buttons displaying the dataset names. + */ + private void showOptionsNotification(int userId, int callbackId, FillResponse response) { + final long token = Binder.clearCallingIdentity(); + try { + showOptionsNotificationAsSystem(userId, callbackId, response); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private void showOptionsNotificationAsSystem(int userId, int callbackId, + FillResponse response) { + // Make sure server callback is removed from cache if user cancels the notification. + final Intent deleteIntent = newNotificationIntent(userId, TYPE_DELETE_CALLBACK); + deleteIntent.putExtra(EXTRA_CALLBACK_ID, callbackId); + final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext, + ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT); + + final String title = "AutoFill Options"; + + final Notification.Builder notification = new Notification.Builder(mContext) + .setCategory(Notification.CATEGORY_SYSTEM) + .setOngoing(false) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setLocalOnly(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setDeleteIntent(deletePendingIntent) + .setContentTitle(title); + + boolean autoCancel = true; + final String subTitle; + final List<Dataset> datasets; + final AutoFillId[] savableIds; + if (response != null) { + datasets = response.getDatasets(); + savableIds = response.getSavableIds(); + } else { + datasets = null; + savableIds = null; + } + boolean showSave = false; + if (datasets == null ) { + subTitle = "No options to auto-fill this activity."; + } else if (datasets.isEmpty()) { + if (savableIds.length == 0) { + subTitle = "No options to auto-fill this activity."; + } else { + subTitle = "No options to auto-fill this activity, but provider can save ids:\n" + + Arrays.toString(savableIds); + showSave = true; + } + } else { + final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId); + if (service == null) { + subTitle = "No auto-fill service for user " + userId; + Slog.w(TAG, subTitle); + } else { + autoCancel = false; + final int size = datasets.size(); + subTitle = "There are " + size + " option(s).\n" + + "Use the notification action(s) to select the proper one."; + for (Dataset dataset : datasets) { + final CharSequence name = dataset.getName(); + final PendingIntent pi = newPickDatasetPI(userId, callbackId, response, dataset); + notification.addAction(new Action.Builder(null, name, pi).build()); + } + } + } + + notification.setAutoCancel(autoCancel); + notification.setStyle(new Notification.BigTextStyle().bigText(subTitle)); + + NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build()); + + if (showSave) { + showSaveNotification(userId, response, null); + } + } + + private void showSaveNotification(int userId, FillResponse response, Dataset dataset) { + final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE); + saveIntent.putExtra(EXTRA_FILL_RESPONSE, response); + if (dataset != null) { + saveIntent.putExtra(EXTRA_DATASET, dataset); + } + final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext, + ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT); + + final String title = "AutoFill Save"; + final String subTitle = "Tap notification to ask provider to save fields: \n" + + Arrays.toString(response.getSavableIds()); + + final Notification notification = new Notification.Builder(mContext) + .setCategory(Notification.CATEGORY_SYSTEM) + .setAutoCancel(true) + .setOngoing(false) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setLocalOnly(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setContentIntent(savePendingIntent) + .setStyle(new Notification.BigTextStyle().bigText(subTitle)) + .build(); + NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification); + } + + ///////////////////////////////////////// + // End of temporary notification code. // + ///////////////////////////////////////// +} diff --git a/services/core/Android.mk b/services/core/Android.mk index efadbefa417a..1366b3b2d694 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -24,7 +24,7 @@ LOCAL_JAVA_LIBRARIES := \ android.hardware.power@1.0-java \ android.hardware.tv.cec@1.0-java -LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update +LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update2 ifneq ($(INCREMENTAL_BUILDS),) LOCAL_PROGUARD_ENABLED := disabled diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index e7f1d16fcc61..2ca1b4eddb49 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -218,6 +218,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { + if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) + && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { + // The relevant restriction has not changed - do nothing. + return; + } final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { @@ -228,6 +233,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { e); } } + updateOppLauncherComponentState(bluetoothDisallowed); } }; @@ -953,7 +959,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - if (isBluetoothDisallowed()) { + final boolean isBluetoothDisallowed = isBluetoothDisallowed(); + updateOppLauncherComponentState(isBluetoothDisallowed); + if (isBluetoothDisallowed) { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { @@ -2011,6 +2019,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not + * offered to the user if Bluetooth is disallowed. Puts the component to its default state if + * Bluetooth is not disallowed. + * + * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user + * restriction was set. + */ + private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", + "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); + final int newState = bluetoothDisallowed + ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED + : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + mContext.getPackageManager() + .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index cef459a3ae39..e23844c3e633 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -301,7 +301,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub { // If we're not connected at all then create a new connection. if (mServiceConnection == null) { - mServiceConnection = new ScoringServiceConnection(componentName); + mServiceConnection = new ScoringServiceConnection(componentName, + scorerData.packageUid); } // Make sure the connection is connected (idempotent) @@ -325,7 +326,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public boolean updateScores(ScoredNetwork[] networks) { - if (!mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid())) { + if (!isCallerActiveScorer(getCallingUid())) { throw new SecurityException("Caller with UID " + getCallingUid() + " is not the active scorer."); } @@ -389,7 +390,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public boolean clearScores() { // Only the active scorer or the system should be allowed to flush all scores. - if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) { + if (isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) { final long token = Binder.clearCallingIdentity(); try { clearInternal(); @@ -418,10 +419,39 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return false; } + /** + * Determine whether the application with the given UID is the enabled scorer. + * + * @param callingUid the UID to check + * @return true if the provided UID is the active scorer, false otherwise. + */ + @Override + public boolean isCallerActiveScorer(int callingUid) { + synchronized (mServiceConnectionLock) { + return mServiceConnection != null && mServiceConnection.mScoringAppUid == callingUid; + } + } + + /** + * Obtain the package name of the current active network scorer. + * + * @return the full package name of the current active scorer, or null if there is no active + * scorer. + */ + @Override + public String getActiveScorerPackage() { + synchronized (mServiceConnectionLock) { + if (mServiceConnection != null) { + return mServiceConnection.mComponentName.getPackageName(); + } + } + return null; + } + @Override public void disableScoring() { // Only the active scorer or the system should be allowed to disable scoring. - if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) { + if (isCallerActiveScorer(getCallingUid()) || isCallerSystemUid()) { // no-op for now but we could write to the setting if needed. } else { throw new SecurityException( @@ -623,12 +653,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private static class ScoringServiceConnection implements ServiceConnection { private final ComponentName mComponentName; + private final int mScoringAppUid; private volatile boolean mBound = false; private volatile boolean mConnected = false; private volatile INetworkRecommendationProvider mRecommendationProvider; - ScoringServiceConnection(ComponentName componentName) { + ScoringServiceConnection(ComponentName componentName, int scoringAppUid) { mComponentName = componentName; + mScoringAppUid = scoringAppUid; } void connect(Context context) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 49c4995bf4dd..3b2041e499ab 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -690,19 +690,21 @@ public class ActivityManagerService extends IActivityManager.Stub public AssistStructure structure = null; public AssistContent content = null; public Bundle receiverExtras; + public int resultCode; public int flags; public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent, - String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _flags, - int _userHandle) { + String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _resultCode, + int _userHandle, int _flags) { activity = _activity; extras = _extras; intent = _intent; hint = _hint; receiver = _receiver; receiverExtras = _receiverExtras; - flags = _flags; + resultCode = _resultCode; userHandle = _userHandle; + flags = _flags; } @Override public void run() { @@ -11837,20 +11839,20 @@ public class ActivityManagerService extends IActivityManager.Stub } synchronized (this) { - if (mStackSupervisor.isUserLockedProfile(userId)) { - final long ident = Binder.clearCallingIdentity(); - try { + final long ident = Binder.clearCallingIdentity(); + try { + if (mUserController.shouldConfirmCredentials(userId)) { final int currentUserId = mUserController.getCurrentUserIdLocked(); - if (mUserController.isLockScreenDisabled(currentUserId)) { - // If there is no device lock, we will show the profile's credential page. - mActivityStarter.showConfirmDeviceCredential(userId); + if (!mKeyguardController.isKeyguardLocked()) { + // If the device is not locked, we will prompt for credentials immediately. + mStackSupervisor.lockAllProfileTasks(userId); } else { // Showing launcher to avoid user entering credential twice. startHomeActivityLocked(currentUserId, "notifyLockedProfile"); } - } finally { - Binder.restoreCallingIdentity(ident); } + } finally { + Binder.restoreCallingIdentity(ident); } } } @@ -12190,7 +12192,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public Bundle getAssistContextExtras(int requestType) { PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null, - null, null, true /* focused */, true /* newSessionId */, + null, 0, null, true /* focused */, true /* newSessionId */, UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0); if (pae == null) { return null; @@ -12258,22 +12260,37 @@ public class ActivityManagerService extends IActivityManager.Stub Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) { return enqueueAssistContext(requestType, null, null, receiver, receiverExtras, - activityToken, focused, newSessionId, - UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) - != null; + 0, activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null, + PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null; } @Override public boolean requestAutoFillData(IResultReceiver receiver, Bundle receiverExtras, - IBinder activityToken, int flags) { - return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_FULL, null, null, receiver, - receiverExtras, activityToken, true, true, - UserHandle.getCallingUserId(), null, PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT, - flags) != null; + int resultCode, IBinder activityToken, int flags) { + final boolean forFill = (flags & View.AUTO_FILL_FLAG_TYPE_FILL) != 0; + final boolean forSave = (flags & View.AUTO_FILL_FLAG_TYPE_SAVE) != 0; + if ((forFill && forSave) || (!forFill) && !(forSave)) { + // There can be only one! + Slog.w(TAG, "requestAutoFillData(): invalid flags (" + flags + ")"); + return false; + } + + // NOTE: we could always use ActivityManager.ASSIST_CONTEXT_FULL and let ActivityThread + // rely on the flags to decide whether the handleRequestAssistContextExtras() is for + // auto-fill, but it's safer to explicitly use new AutoFill types, in case the Assist + // requests use flags in the future as well (since their flags value might collide with the + // auto-fill flag values). + final int type = forFill? + ActivityManager.ASSIST_CONTEXT_AUTO_FILL : + ActivityManager.ASSIST_CONTEXT_AUTO_FILL_SAVE; + + return enqueueAssistContext(type, null, null, receiver, receiverExtras, resultCode, + activityToken, true, true, UserHandle.getCallingUserId(), null, + PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null; } private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint, - IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken, + IResultReceiver receiver, Bundle receiverExtras, int resultCode, IBinder activityToken, boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout, int flags) { enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO, @@ -12314,7 +12331,7 @@ public class ActivityManagerService extends IActivityManager.Stub extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName); extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid); pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras, - flags, userHandle); + resultCode, userHandle, flags); // Increment the sessionId if necessary if (newSessionId) { mViSessionId++; @@ -12400,17 +12417,15 @@ public class ActivityManagerService extends IActivityManager.Stub if (pae.flags > 0) { sendBundle.putInt(VoiceInteractionSession.KEY_FLAGS, pae.flags); } - IBinder autoFillCallback = - extras.getBinder(AutoFillService.KEY_CALLBACK); - if (autoFillCallback != null) { - sendBundle.putBinder(AutoFillService.KEY_CALLBACK, - autoFillCallback); + IBinder cb = extras.getBinder(AutoFillService.KEY_CALLBACK); + if (cb != null) { + sendBundle.putBinder(AutoFillService.KEY_CALLBACK, cb); } } } if (sendReceiver != null) { try { - sendReceiver.send(0, sendBundle); + sendReceiver.send(pae.resultCode, sendBundle); } catch (RemoteException e) { } return; @@ -12435,9 +12450,9 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle, Bundle args) { - return enqueueAssistContext(requestType, intent, hint, null, null, null, - true /* focused */, true /* newSessionId */, - userHandle, args, PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null; + return enqueueAssistContext(requestType, intent, hint, null, null, 0, null, + true /* focused */, true /* newSessionId */, userHandle, args, + PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null; } public void registerProcessObserver(IProcessObserver observer) { @@ -17875,6 +17890,11 @@ public class ActivityManagerService extends IActivityManager.Stub private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp, String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) { + if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) { + // Don't yell about broadcasts sent via shell + return; + } + final String action = intent.getAction(); if (isProtectedBroadcast || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) @@ -18018,11 +18038,7 @@ public class ActivityManagerService extends IActivityManager.Stub case Process.PHONE_UID: case Process.BLUETOOTH_UID: case Process.NFC_UID: - if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) { - isCallerSystem = false; - } else { - isCallerSystem = true; - } + isCallerSystem = true; break; default: isCallerSystem = (callerApp != null) && callerApp.persistent; diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 3f166fe4ddfb..e46d20457706 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -172,7 +172,7 @@ class ActivityMetricsLogger { MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, (int) (SystemClock.uptimeMillis() / 1000)); - LogBuilder builder = new LogBuilder(); + LogBuilder builder = new LogBuilder(MetricsEvent.APP_TRANSITION); builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName); builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0); builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8bd7c9010181..a93524903b29 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -763,43 +763,52 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * TODO: Handle freefom mode. - * @return true when credential confirmation is needed for the user and there is any - * activity started by the user in any visible stack. + * Detects whether we should show a lock screen in front of this task for a locked user. + * <p> + * We'll do this if either of the following holds: + * <ul> + * <li>The top activity explicitly belongs to {@param userId}.</li> + * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> + * </ul> + * + * @return {@code true} if the top activity looks like it belongs to {@param userId}. */ - boolean isUserLockedProfile(@UserIdInt int userId) { - if (!mService.mUserController.shouldConfirmCredentials(userId)) { - return false; - } - final ActivityStack fullScreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID); - final ActivityStack dockedStack = getStack(DOCKED_STACK_ID); - final ActivityStack[] activityStacks = new ActivityStack[] {fullScreenStack, dockedStack}; - for (final ActivityStack activityStack : activityStacks) { - if (activityStack == null) { - continue; - } - if (activityStack.topRunningActivityLocked() == null) { - continue; - } - if (activityStack.getStackVisibilityLocked(null) == STACK_INVISIBLE) { - continue; - } - if (activityStack.isDockedStack() && mIsDockMinimized) { - continue; - } - final TaskRecord topTask = activityStack.topTask(); - if (topTask == null) { - continue; - } - // To handle the case that work app is in the task but just is not the top one. - for (int i = topTask.mActivities.size() - 1; i >= 0; i--) { - final ActivityRecord activityRecord = topTask.mActivities.get(i); - if (activityRecord.userId == userId) { - return true; + private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) { + // To handle the case that work app is in the task but just is not the top one. + final ActivityRecord activityRecord = task.getTopActivity(); + final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); + + return (activityRecord != null && activityRecord.userId == userId) + || (resultTo != null && resultTo.userId == userId); + } + + /** + * Find all visible task stacks containing {@param userId} and intercept them with an activity + * to block out the contents and possibly start a credential-confirming intent. + * + * @param userId user handle for the locked managed profile. + */ + void lockAllProfileTasks(@UserIdInt int userId) { + mWindowManager.deferSurfaceLayout(); + try { + final List<ActivityStack> stacks = getStacks(); + for (int stackNdx = stacks.size() - 1; stackNdx >= 0; stackNdx--) { + final List<TaskRecord> tasks = stacks.get(stackNdx).getAllTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) { + final TaskRecord task = tasks.get(taskNdx); + + // Check the task for a top activity belonging to userId, or returning a result + // to an activity belonging to userId. Example case: a document picker for + // personal files, opened by a work app, should still get locked. + if (taskTopActivityIsUser(task, userId)) { + mService.mTaskChangeNotificationController.notifyTaskProfileLocked( + task.taskId, userId); + } } } + } finally { + mWindowManager.continueSurfaceLayout(); } - return false; } void setNextTaskIdForUserLocked(int taskId, int userId) { diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index fd248c6c807a..fbdbb1b2443a 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -39,6 +39,7 @@ class TaskChangeNotificationController { static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11; static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12; static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; + static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; // Delay in notifying task stack change listeners (in millis) static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -110,6 +111,9 @@ class TaskChangeNotificationController { case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG: forAllListeners((listener) -> listener.onActivityDismissingDockedStack()); break; + case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG: + forAllListeners((listener) -> listener.onTaskProfileLocked(msg.arg1, msg.arg2)); + break; } } } @@ -228,4 +232,13 @@ class TaskChangeNotificationController { mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, 0 /* unused */) .sendToTarget(); } + + /** + * Notify listeners that the task has been put in a locked state because one or more of the + * activities inside it belong to a managed profile user that has been locked. + */ + void notifyTaskProfileLocked(int taskId, int userId) { + mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG, taskId, userId) + .sendToTarget(); + } } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 6d96a1015aa2..79567d50c31c 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -199,7 +199,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); mTetherMasterSM.start(); - mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(); + mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( + mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); @@ -1027,38 +1028,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } /** - * A NetworkCallback class that relays information of interest to the - * tethering master state machine thread for subsequent processing. - */ - class UpstreamNetworkCallback extends NetworkCallback { - @Override - public void onAvailable(Network network) { - mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_AVAILABLE, 0, network); - } - - @Override - public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) { - mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES, 0, - new NetworkState(null, null, newNc, network, null, null)); - } - - @Override - public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { - mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, 0, - new NetworkState(null, newLp, null, network, null, null)); - } - - @Override - public void onLost(Network network) { - mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_CALLBACK, - UpstreamNetworkMonitor.EVENT_ON_LOST, 0, network); - } - } - - /** * A class to centralize all the network and link properties information * pertaining to the current and any potential upstream network. * @@ -1072,21 +1041,31 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering * TODO: Investigate whether more "upstream-specific" logic/functionality * could/should be moved here. */ - class UpstreamNetworkMonitor { - static final int EVENT_ON_AVAILABLE = 1; - static final int EVENT_ON_CAPABILITIES = 2; - static final int EVENT_ON_LINKPROPERTIES = 3; - static final int EVENT_ON_LOST = 4; - - final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); - NetworkCallback mDefaultNetworkCallback; - NetworkCallback mDunTetheringCallback; - - void start() { + public class UpstreamNetworkMonitor { + public static final int EVENT_ON_AVAILABLE = 1; + public static final int EVENT_ON_CAPABILITIES = 2; + public static final int EVENT_ON_LINKPROPERTIES = 3; + public static final int EVENT_ON_LOST = 4; + + private final Context mContext; + private final StateMachine mTarget; + private final int mWhat; + private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); + private ConnectivityManager mCM; + private NetworkCallback mDefaultNetworkCallback; + private NetworkCallback mDunTetheringCallback; + + public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) { + mContext = ctx; + mTarget = tgt; + mWhat = what; + } + + public void start() { stop(); mDefaultNetworkCallback = new UpstreamNetworkCallback(); - getConnectivityManager().registerDefaultNetworkCallback(mDefaultNetworkCallback); + cm().registerDefaultNetworkCallback(mDefaultNetworkCallback); final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -1094,29 +1073,28 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) .build(); mDunTetheringCallback = new UpstreamNetworkCallback(); - getConnectivityManager().registerNetworkCallback( - dunTetheringRequest, mDunTetheringCallback); + cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback); } - void stop() { + public void stop() { if (mDefaultNetworkCallback != null) { - getConnectivityManager().unregisterNetworkCallback(mDefaultNetworkCallback); + cm().unregisterNetworkCallback(mDefaultNetworkCallback); mDefaultNetworkCallback = null; } if (mDunTetheringCallback != null) { - getConnectivityManager().unregisterNetworkCallback(mDunTetheringCallback); + cm().unregisterNetworkCallback(mDunTetheringCallback); mDunTetheringCallback = null; } mNetworkMap.clear(); } - NetworkState lookup(Network network) { + public NetworkState lookup(Network network) { return (network != null) ? mNetworkMap.get(network) : null; } - NetworkState processCallback(int arg1, Object obj) { + public NetworkState processCallback(int arg1, Object obj) { switch (arg1) { case EVENT_ON_AVAILABLE: { final Network network = (Network) obj; @@ -1128,7 +1106,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering new NetworkState(null, null, null, network, null, null)); } - final ConnectivityManager cm = getConnectivityManager(); + final ConnectivityManager cm = cm(); if (mDefaultNetworkCallback != null) { cm.requestNetworkCapabilities(mDefaultNetworkCallback); @@ -1199,6 +1177,42 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return null; } } + + // Fetch (and cache) a ConnectivityManager only if and when we need one. + private ConnectivityManager cm() { + if (mCM == null) { + mCM = mContext.getSystemService(ConnectivityManager.class); + } + return mCM; + } + + /** + * A NetworkCallback class that relays information of interest to the + * tethering master state machine thread for subsequent processing. + */ + private class UpstreamNetworkCallback extends NetworkCallback { + @Override + public void onAvailable(Network network) { + mTarget.sendMessage(mWhat, EVENT_ON_AVAILABLE, 0, network); + } + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) { + mTarget.sendMessage(mWhat, EVENT_ON_CAPABILITIES, 0, + new NetworkState(null, null, newNc, network, null, null)); + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { + mTarget.sendMessage(mWhat, EVENT_ON_LINKPROPERTIES, 0, + new NetworkState(null, newLp, null, network, null, null)); + } + + @Override + public void onLost(Network network) { + mTarget.sendMessage(mWhat, EVENT_ON_LOST, 0, network); + } + } } // Needed because the canonical source of upstream truth is just the diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 477ecdf60c48..015345c23a1a 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -577,6 +577,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightness = mPowerRequest.dozeScreenBrightness; } break; + case DisplayPowerRequest.POLICY_VR: + state = Display.STATE_VR; + break; case DisplayPowerRequest.POLICY_DIM: case DisplayPowerRequest.POLICY_BRIGHT: default: @@ -618,6 +621,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen state change unless already animating. // The transition may be deferred, so after this point we will use the // actual state instead of the desired one. + final int oldState = mPowerState.getScreenState(); animateScreenStateChange(state, performScreenOffTransition); state = mPowerState.getScreenState(); @@ -717,9 +721,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Animate the screen brightness when the screen is on or dozing. - // Skip the animation when the screen is off or suspended. + // Skip the animation when the screen is off or suspended or transition to/from VR. if (!mPendingScreenOff) { - if (state == Display.STATE_ON || state == Display.STATE_DOZE) { + boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR); + if ((state == Display.STATE_ON || state == Display.STATE_DOZE) && !wasOrWillBeInVr) { animateScreenBrightness(brightness, slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast); } else { @@ -903,6 +908,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPowerState.setColorFadeLevel(1.0f); mPowerState.dismissColorFade(); } + } else if (target == Display.STATE_VR) { + // Wait for brightness animation to complete beforehand when entering VR + // from screen on to prevent a perceptible jump because brightness may operate + // differently when the display is configured for dozing. + if (mScreenBrightnessRampAnimator.isAnimating() + && mPowerState.getScreenState() == Display.STATE_ON) { + return; + } + + // Set screen state. + if (!setScreenState(Display.STATE_VR)) { + return; // screen on blocked + } + + // Dismiss the black surface without fanfare. + mPowerState.setColorFadeLevel(1.0f); + mPowerState.dismissColorFade(); } else if (target == Display.STATE_DOZE) { // Want screen dozing. // Wait for brightness animation to complete beforehand when entering doze diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 61c2eacaa8f9..867322578a66 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -471,6 +471,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + // If the state change was from or to VR, then we need to tell the light + // so that it can apply appropriate VR brightness settings. This should + // happen prior to changing the brightness but also if there is no + // brightness change at all. + if ((state == Display.STATE_VR || currentState == Display.STATE_VR) && + currentState != state) { + setVrMode(state == Display.STATE_VR); + } + + // Apply brightness changes given that we are in a non-suspended state. if (brightnessChanged) { setDisplayBrightness(brightness); @@ -482,6 +492,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + private void setVrMode(boolean isVrEnabled) { + if (DEBUG) { + Slog.d(TAG, "setVrMode(" + + "id=" + displayId + + ", state=" + Display.stateToString(state) + ")"); + } + mBacklight.setVrMode(isVrEnabled); + } + private void setDisplayState(int state) { if (DEBUG) { Slog.d(TAG, "setDisplayState(" diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/Light.java index 0bab86b67e82..b5ec603f5f70 100644 --- a/services/core/java/com/android/server/lights/Light.java +++ b/services/core/java/com/android/server/lights/Light.java @@ -46,4 +46,5 @@ public abstract class Light { public abstract void pulse(); public abstract void pulse(int color, int onMS); public abstract void turnOff(); + public abstract void setVrMode(boolean enabled); } diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index bba0a50e7af4..e07156ece1b9 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -1,5 +1,4 @@ -/* - * Copyright (C) 2008 The Android Open Source Project +/* * Copyright (C) 2008 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. @@ -17,18 +16,13 @@ package com.android.server.lights; import com.android.server.SystemService; -import com.android.server.vr.VrManagerService; import android.app.ActivityManager; import android.content.Context; import android.os.Handler; import android.os.Message; -import android.os.RemoteException; import android.os.Trace; -import android.os.UserHandle; import android.provider.Settings; -import android.service.vr.IVrManager; -import android.service.vr.IVrStateCallbacks; import android.util.Slog; public class LightsService extends SystemService { @@ -36,7 +30,6 @@ public class LightsService extends SystemService { static final boolean DEBUG = false; final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT]; - private boolean mVrModeEnabled; private final class LightImpl extends Light { @@ -52,6 +45,13 @@ public class LightsService extends SystemService { @Override public void setBrightness(int brightness, int brightnessMode) { synchronized (this) { + // LOW_PERSISTENCE cannot be manually set + if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { + Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId + + ": brightness=0x" + Integer.toHexString(brightness)); + return; + } + int color = brightness & 0x000000ff; color = 0xff000000 | (color << 16) | (color << 8) | color; setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); @@ -80,11 +80,9 @@ public class LightsService extends SystemService { @Override public void pulse(int color, int onMS) { synchronized (this) { - if (mBrightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { - return; - } if (mColor == 0 && !mFlashing) { - setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER); + setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, + BRIGHTNESS_MODE_USER); mColor = 0; mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS); } @@ -98,17 +96,23 @@ public class LightsService extends SystemService { } } - void enableLowPersistence() { - synchronized(this) { - setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_LOW_PERSISTENCE); - mLocked = true; - } - } - - void disableLowPersistence() { - synchronized(this) { - mLocked = false; - setLightLocked(mLastColor, LIGHT_FLASH_NONE, 0, 0, mLastBrightnessMode); + @Override + public void setVrMode(boolean enabled) { + synchronized (this) { + if (mVrModeEnabled != enabled) { + mVrModeEnabled = enabled; + + mUseLowPersistenceForVR = + (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE); + if (shouldBeInLowPersistenceMode()) { + mLastBrightnessMode = mBrightnessMode; + } + + // NOTE: We do not trigger a call to setLightLocked here. We do not know the + // current brightness or other values when leaving VR so we avoid any incorrect + // jumps. The code that calls this method will immediately issue a brightness + // update which is when the change will occur. + } } } @@ -119,7 +123,13 @@ public class LightsService extends SystemService { } private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { - if (!mLocked && (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS || + if (shouldBeInLowPersistenceMode()) { + brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE; + } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { + brightnessMode = mLastBrightnessMode; + } + + if ((color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS || mBrightnessMode != brightnessMode)) { if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#" + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode); @@ -128,7 +138,6 @@ public class LightsService extends SystemService { mMode = mode; mOnMS = onMS; mOffMS = offMS; - mLastBrightnessMode = mBrightnessMode; mBrightnessMode = brightnessMode; Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x" + Integer.toHexString(color) + ")"); @@ -140,6 +149,10 @@ public class LightsService extends SystemService { } } + private boolean shouldBeInLowPersistenceMode() { + return mVrModeEnabled && mUseLowPersistenceForVR; + } + private int mId; private int mColor; private int mMode; @@ -149,7 +162,8 @@ public class LightsService extends SystemService { private int mBrightnessMode; private int mLastBrightnessMode; private int mLastColor; - private boolean mLocked; + private boolean mVrModeEnabled; + private boolean mUseLowPersistenceForVR; } public LightsService(Context context) { @@ -167,17 +181,6 @@ public class LightsService extends SystemService { @Override public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - IVrManager vrManager = - (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE); - if (vrManager != null) { - try { - vrManager.registerListener(mVrStateCallbacks); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to register VR mode state listener: " + e); - } - } - } } private int getVrDisplayMode() { @@ -188,30 +191,6 @@ public class LightsService extends SystemService { currentUser); } - private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { - @Override - public void onVrStateChanged(boolean enabled) throws RemoteException { - LightImpl l = mLights[LightsManager.LIGHT_ID_BACKLIGHT]; - int vrDisplayMode = getVrDisplayMode(); - - // User leaves VR mode before altering display settings. - if (enabled && vrDisplayMode == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE) { - if (!mVrModeEnabled) { - if (DEBUG) - Slog.v(TAG, "VR mode enabled, setting brightness to low persistence"); - l.enableLowPersistence(); - mVrModeEnabled = true; - } - } else { - if (mVrModeEnabled) { - if (DEBUG) Slog.v(TAG, "VR mode disabled, resetting brightnes"); - l.disableLowPersistence(); - mVrModeEnabled = false; - } - } - } - }; - private final LightsManager mService = new LightsManager() { @Override public Light getLight(int id) { diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 2bf5ef10ec89..601a2194e8f3 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -42,12 +42,18 @@ import java.util.concurrent.TimeUnit; * {@hide} */ public class BackgroundDexOptService extends JobService { - static final String TAG = "BackgroundDexOptService"; + private static final String TAG = "BackgroundDexOptService"; - static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR; + private static final boolean DEBUG = false; - static final int JOB_IDLE_OPTIMIZE = 800; - static final int JOB_POST_BOOT_UPDATE = 801; + private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR; + + private static final int JOB_IDLE_OPTIMIZE = 800; + private static final int JOB_POST_BOOT_UPDATE = 801; + + private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG + ? TimeUnit.MINUTES.toMillis(1) + : TimeUnit.DAYS.toMillis(1); private static ComponentName sDexoptServiceName = new ComponentName( "android", @@ -86,7 +92,7 @@ public class BackgroundDexOptService extends JobService { js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName) .setRequiresDeviceIdle(true) .setRequiresCharging(true) - .setPeriodic(TimeUnit.DAYS.toMillis(1)) + .setPeriodic(IDLE_OPTIMIZATION_PERIOD) .build()); if (DEBUG_DEXOPT) { @@ -208,6 +214,7 @@ public class BackgroundDexOptService extends JobService { private void idleOptimization(JobParameters jobParams, PackageManagerService pm, ArraySet<String> pkgs) { + Log.i(TAG, "Performing idle optimizations"); // If post-boot update is still running, request that it exits early. mExitPostBootUpdate.set(true); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 2e0199b0b863..8c4a95ce1cdf 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -217,26 +217,11 @@ class PackageDexOptimizer { dexoptNeeded); } - final String dexoptType; - String oatDir = null; - boolean isOdexLocation = (dexoptNeeded < 0); - switch (Math.abs(dexoptNeeded)) { - case DexFile.NO_DEXOPT_NEEDED: - continue; - case DexFile.DEX2OAT_FROM_SCRATCH: - case DexFile.DEX2OAT_FOR_BOOT_IMAGE: - case DexFile.DEX2OAT_FOR_FILTER: - case DexFile.DEX2OAT_FOR_RELOCATION: - dexoptType = "dex2oat"; - oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); - break; - case DexFile.PATCHOAT_FOR_RELOCATION: - dexoptType = "patchoat"; - break; - default: - throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded); + if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { + continue; } + String oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); String sharedLibrariesPath = null; if (sharedLibraries != null && sharedLibraries.length != 0) { StringBuilder sb = new StringBuilder(); @@ -248,7 +233,7 @@ class PackageDexOptimizer { } sharedLibrariesPath = sb.toString(); } - Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" + Log.i(TAG, "Running dexopt on: " + path + " pkg=" + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable + " target-filter=" + targetCompilerFilter + " oatDir=" + oatDir diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index ddbc5fa59e16..3085c9cc9d29 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -361,6 +361,11 @@ class ShortcutPackage extends ShortcutPackageItem { } oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis()); + // See ShortcutRequestPinProcessor.directPinShortcut(). + if (mShortcutUser.mService.isDummyMainActivity(oldShortcut.getActivity())) { + oldShortcut.setActivity(null); + } + return oldShortcut; } else { deleteShortcutInner(shortcutId); @@ -1515,6 +1520,8 @@ class ShortcutPackage extends ShortcutPackageItem { boolean failed = false; + final ShortcutService s = mShortcutUser.mService; + final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all = sortShortcutsToActivities(); @@ -1554,10 +1561,10 @@ class ShortcutPackage extends ShortcutPackageItem { Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " is both dynamic and manifest at the same time."); } - if (si.getActivity() == null) { + if (si.getActivity() == null && !si.isFloating()) { failed = true; Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() - + " has null activity."); + + " has null activity, but not floating."); } if ((si.isDynamic() || si.isManifestShortcut()) && !si.isEnabled()) { failed = true; @@ -1579,6 +1586,11 @@ class ShortcutPackage extends ShortcutPackageItem { Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + " has both resource and bitmap icons"); } + if (s.isDummyMainActivity(si.getActivity())) { + failed = true; + Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId() + + " has a dummy target activity"); + } } if (failed) { diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java index e815f0ae18fd..c8ddf0a711bf 100644 --- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java +++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java @@ -16,7 +16,6 @@ package com.android.server.pm; import android.annotation.Nullable; -import android.app.PendingIntent; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Intent; @@ -209,7 +208,7 @@ class ShortcutRequestPinProcessor { final boolean existsAlready = existing != null; if (DEBUG) { - Slog.d(TAG, "requestPinnedShortcut package=" + inShortcut.getPackage() + Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage() + " existsAlready=" + existsAlready + " shortcut=" + inShortcut.toInsecureString()); } @@ -237,6 +236,14 @@ class ShortcutRequestPinProcessor { // FLAG_PINNED is still set, if it's pinned by other launchers. shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED); } else { + // If the shortcut has no default activity, try to set the main activity. + // But in the request-pin case, it's optional, so it's okay even if the caller + // has no default activity. + if (inShortcut.getActivity() == null) { + inShortcut.setActivity(mService.injectGetDefaultMainActivity( + inShortcut.getPackage(), inShortcut.getUserId())); + } + // It doesn't exist, so it must have all mandatory fields. mService.validateShortcutForPinRequest(inShortcut); @@ -244,12 +251,15 @@ class ShortcutRequestPinProcessor { inShortcut.resolveResourceStrings(mService.injectGetResourcesForApplicationAsUser( inShortcut.getPackage(), inShortcut.getUserId())); if (DEBUG) { - Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString()); + Slog.d(TAG, "Resolved shortcut=" + inShortcut.toInsecureString()); } // We should strip out the intent, but should preserve the icon. shortcutForLauncher = inShortcut.clone( ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL); } + if (DEBUG) { + Slog.d(TAG, "Sending to launcher=" + shortcutForLauncher.toInsecureString()); + } // Create a request object. final PinShortcutRequestInner inner = @@ -360,7 +370,9 @@ class ShortcutRequestPinProcessor { if (DEBUG) { Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic"); } - // Add as a dynamic shortcut. + // Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must + // have a target activity, so we set a dummy here. It's later removed + // in deleteDynamicWithId(). if (original.getActivity() == null) { original.setActivity(mService.getDummyMainActivity(appPackageName)); } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index c02ce6e0daeb..a890526c0f91 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1604,7 +1604,7 @@ public class ShortcutService extends IShortcutService.Stub { private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate, boolean forPinRequest) { Preconditions.checkNotNull(shortcut, "Null shortcut detected"); - if (!forPinRequest && shortcut.getActivity() != null) { + if (shortcut.getActivity() != null) { Preconditions.checkState( shortcut.getPackage().equals(shortcut.getActivity().getPackageName()), "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not" @@ -1618,10 +1618,8 @@ public class ShortcutService extends IShortcutService.Stub { if (!forUpdate) { shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest); if (!forPinRequest) { - Preconditions.checkArgument( - injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()), - "Cannot publish shortcut: " + shortcut.getActivity() - + " is not main activity"); + Preconditions.checkState(shortcut.getActivity() != null, + "Cannot publish shortcut: target activity is not set"); } } if (shortcut.getIcon() != null) { @@ -1870,9 +1868,7 @@ public class ShortcutService extends IShortcutService.Stub { throwIfUserLockedL(userId); Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()), - "Calling application must have a foreground activity or a foreground service"); - - // TODO Cancel all pending requests from the caller. + "Calling application must have a foreground activity or a foreground service"); // Send request to the launcher, if supported. ret = mShortcutRequestPinProcessor.requestPinItemLocked(shortcut, appWidget, userId, @@ -3193,6 +3189,10 @@ public class ShortcutService extends IShortcutService.Stub { return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY); } + boolean isDummyMainActivity(@Nullable ComponentName name) { + return name != null && DUMMY_MAIN_ACTIVITY.equals(name.getClassName()); + } + /** * Return all the enabled, exported and main activities from a package. */ diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index fa1d991fa39a..0a312f0ad7ee 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -251,9 +251,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final boolean DEBUG_INPUT = false; static final boolean DEBUG_KEYGUARD = false; static final boolean DEBUG_LAYOUT = false; - static final boolean DEBUG_STARTING_WINDOW = false; + static final boolean DEBUG_SPLASH_SCREEN = false; static final boolean DEBUG_WAKEUP = false; - static final boolean SHOW_STARTING_ANIMATIONS = true; + static final boolean SHOW_SPLASH_SCREENS = true; // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. // No longer recommended for desk docks; @@ -2794,10 +2794,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public View addStartingWindow(IBinder appToken, String packageName, int theme, - CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, - int icon, int logo, int windowFlags, Configuration overrideConfig) { - if (!SHOW_STARTING_ANIMATIONS) { + public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, + CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, + int logo, int windowFlags, Configuration overrideConfig) { + if (!SHOW_SPLASH_SCREENS) { return null; } if (packageName == null) { @@ -2809,7 +2809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { try { Context context = mContext; - if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow " + packageName + if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen " + packageName + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme=" + Integer.toHexString(theme)); if (theme != context.getThemeResId() || labelRes != 0) { @@ -2822,8 +2822,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (overrideConfig != null && !overrideConfig.equals(EMPTY)) { - if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: creating context based" - + " on overrideConfig" + overrideConfig + " for starting window"); + if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: creating context based" + + " on overrideConfig" + overrideConfig + " for splash screen"); final Context overrideContext = context.createConfigurationContext(overrideConfig); overrideContext.setTheme(theme); final TypedArray typedArray = overrideContext.obtainStyledAttributes( @@ -2833,7 +2833,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // We want to use the windowBackground for the override context if it is // available, otherwise we use the default one to make sure a themed starting // window is displayed for the app. - if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "addStartingWindow: apply overrideConfig" + if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "addSplashScreen: apply overrideConfig" + overrideConfig + " to starting window resId=" + resId); context = overrideContext; } @@ -2895,19 +2895,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; } - params.setTitle("Starting " + packageName); + params.setTitle("Splash Screen " + packageName); wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); view = win.getDecorView(); - if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Adding starting window for " + if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for " + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null)); wm.addView(view, params); // Only return the view if it was successfully added to the // window manager... which we can tell by it having a parent. - return view.getParent() != null ? view : null; + return view.getParent() != null ? new SplashScreenSurface(view) : null; } catch (WindowManager.BadTokenException e) { // ignore Log.w(TAG, appToken + " already running, starting window not displayed. " + @@ -2929,13 +2929,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void removeStartingWindow(IBinder appToken, View window) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": " - + window + " Callers=" + Debug.getCallers(4)); + public void removeSplashScreen(IBinder appToken, StartingSurface surface) { + if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": " + + surface + " Callers=" + Debug.getCallers(4)); - if (window != null) { + if (surface != null) { WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); - wm.removeView(window); + wm.removeView(((SplashScreenSurface) surface).view); } } diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java new file mode 100644 index 000000000000..d4212915c0a6 --- /dev/null +++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 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.policy; + +import android.view.View; +import android.view.WindowManagerPolicy; +import android.view.WindowManagerPolicy.StartingSurface; + +import com.android.internal.policy.DecorView; +import com.android.internal.policy.PhoneWindow; + +/** + * Holds the contents of a splash screen starting window, i.e. the {@link DecorView} of a + * {@link PhoneWindow}. This is just a wrapper such that we can return it from + * {@link WindowManagerPolicy#addSplashScreen}. + */ +class SplashScreenSurface implements StartingSurface { + + final View view; + + SplashScreenSurface(View view) { + this.view = view; + } +} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index f2ccac5197aa..8aefebc9f58d 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -136,6 +136,8 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11; // Dirty bit: sQuiescent changed private static final int DIRTY_QUIESCENT = 1 << 12; + // Dirty bit: VR Mode enabled changed + private static final int DIRTY_VR_MODE_CHANGED = 1 << 13; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -413,11 +415,15 @@ public final class PowerManagerService extends SystemService private int mScreenBrightnessSettingMinimum; private int mScreenBrightnessSettingMaximum; private int mScreenBrightnessSettingDefault; + private int mScreenBrightnessForVrSettingDefault; // The screen brightness setting, from 0 to 255. // Use -1 if no value has been set. private int mScreenBrightnessSetting; + // The screen brightness setting, from 0 to 255, to be used while in VR Mode. + private int mScreenBrightnessForVrSetting; + // The screen auto-brightness adjustment setting, from -1 to 1. // Use 0 if there is no adjustment. private float mScreenAutoBrightnessAdjustmentSetting; @@ -511,6 +517,9 @@ public final class PowerManagerService extends SystemService // True if brightness should be affected by twilight. private boolean mBrightnessUseTwilight; + // True if we are currently in VR Mode. + private boolean mIsVrModeEnabled; + private native void nativeInit(); private static native void nativeAcquireSuspendBlocker(String name); @@ -594,6 +603,7 @@ public final class PowerManagerService extends SystemService mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); + mScreenBrightnessForVrSettingDefault = pm.getDefaultScreenBrightnessForVrSetting(); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -640,6 +650,9 @@ public final class PowerManagerService extends SystemService Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_BRIGHTNESS_FOR_VR), + false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( @@ -773,11 +786,17 @@ public final class PowerManagerService extends SystemService } } - final int oldScreenBrightnessSetting = mScreenBrightnessSetting; + final int oldScreenBrightnessSetting = getCurrentBrightnessSettingLocked(); + + mScreenBrightnessForVrSetting = Settings.System.getIntForUser(resolver, + Settings.System.SCREEN_BRIGHTNESS_FOR_VR, mScreenBrightnessForVrSettingDefault, + UserHandle.USER_CURRENT); + mScreenBrightnessSetting = Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault, UserHandle.USER_CURRENT); - if (oldScreenBrightnessSetting != mScreenBrightnessSetting) { + + if (oldScreenBrightnessSetting != getCurrentBrightnessSettingLocked()) { mTemporaryScreenBrightnessSettingOverride = -1; } @@ -811,6 +830,10 @@ public final class PowerManagerService extends SystemService mDirty |= DIRTY_SETTINGS; } + private int getCurrentBrightnessSettingLocked() { + return mIsVrModeEnabled ? mScreenBrightnessForVrSetting : mScreenBrightnessSetting; + } + private void postAfterBootCompleted(Runnable r) { if (mBootCompleted) { BackgroundThread.getHandler().post(r); @@ -2069,6 +2092,7 @@ public final class PowerManagerService extends SystemService || !mDreamsSupportedConfig || !mDreamsEnabledSetting || !mDisplayPowerRequest.isBrightOrDim() + || mDisplayPowerRequest.isVr() || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0 || !mBootCompleted) { @@ -2113,7 +2137,8 @@ public final class PowerManagerService extends SystemService final boolean oldDisplayReady = mDisplayReady; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED - | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_QUIESCENT)) != 0) { + | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | + DIRTY_QUIESCENT)) != 0) { mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); // Determine appropriate screen brightness and auto-brightness adjustments. @@ -2127,6 +2152,9 @@ public final class PowerManagerService extends SystemService // bootloader brightness and the default brightness to be identical. autoBrightness = false; brightnessSetByUser = false; + } else if (mIsVrModeEnabled) { + screenBrightness = mScreenBrightnessForVrSetting; + autoBrightness = false; } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { screenBrightness = mScreenBrightnessOverrideFromWindowManager; autoBrightness = false; @@ -2160,7 +2188,7 @@ public final class PowerManagerService extends SystemService mDisplayPowerRequest.useAutoBrightness = autoBrightness; mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled; - mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress; + mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); mDisplayPowerRequest.useTwilight = mBrightnessUseTwilight; if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { @@ -2191,6 +2219,7 @@ public final class PowerManagerService extends SystemService + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) + ", mBootCompleted=" + mBootCompleted + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress + + ", mIsVrModeEnabled= " + mIsVrModeEnabled + ", sQuiescent=" + sQuiescent); } } @@ -2220,6 +2249,10 @@ public final class PowerManagerService extends SystemService } } + private boolean shouldBoostScreenBrightness() { + return !mIsVrModeEnabled && mScreenBrightnessBoostInProgress; + } + private static boolean isValidBrightness(int value) { return value >= 0 && value <= 255; } @@ -2230,6 +2263,10 @@ public final class PowerManagerService extends SystemService } private int getDesiredScreenPolicyLocked() { + if (mIsVrModeEnabled) { + return DisplayPowerRequest.POLICY_VR; + } + if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } @@ -2333,7 +2370,7 @@ public final class PowerManagerService extends SystemService }; private boolean shouldUseProximitySensorLocked() { - return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0; + return !mIsVrModeEnabled && (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0; } /** @@ -3085,7 +3122,11 @@ public final class PowerManagerService extends SystemService pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum); pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + pw.println(" mScreenBrightnessForVrSettingDefault=" + + mScreenBrightnessForVrSettingDefault); + pw.println(" mScreenBrightnessForVrSetting=" + mScreenBrightnessForVrSetting); pw.println(" mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled); + pw.println(" mIsVrModeEnabled=" + mIsVrModeEnabled); final int sleepTimeout = getSleepTimeoutLocked(); final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); @@ -3223,6 +3264,14 @@ public final class PowerManagerService extends SystemService @Override public void onVrStateChanged(boolean enabled) { powerHintInternal(PowerHint.VR_MODE, enabled ? 1 : 0); + + synchronized (mLock) { + if (mIsVrModeEnabled != enabled) { + mIsVrModeEnabled = enabled; + mDirty |= DIRTY_VR_MODE_CHANGED; + updatePowerStateLocked(); + } + } } }; @@ -3975,6 +4024,7 @@ public final class PowerManagerService extends SystemService case Display.STATE_DOZE: case Display.STATE_DOZE_SUSPEND: case Display.STATE_ON: + case Display.STATE_VR: break; default: screenState = Display.STATE_UNKNOWN; diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java index b260e4e2fc34..b704eb1d991c 100644 --- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java +++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java @@ -20,7 +20,7 @@ import android.util.Slog; import java.io.File; import java.io.IOException; -import libcore.tzdata.update.TzDataBundleInstaller; +import libcore.tzdata.update2.TimeZoneBundleInstaller; /** * An install receiver responsible for installing timezone data updates. @@ -29,18 +29,19 @@ public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver { private static final String TAG = "TZDataInstallReceiver"; + private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata"); private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo"); private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/"; private static final String UPDATE_METADATA_DIR_NAME = "metadata/"; private static final String UPDATE_VERSION_FILE_NAME = "version"; private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip"; - private final TzDataBundleInstaller installer; + private final TimeZoneBundleInstaller installer; public TzDataInstallReceiver() { super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME, UPDATE_VERSION_FILE_NAME); - installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR); + installer = new TimeZoneBundleInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR); } @Override diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 35004c295681..cab39b56c3e9 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -27,21 +27,21 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WIND import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.H.ADD_STARTING; - -import android.graphics.Bitmap; -import android.os.Trace; -import com.android.server.AttributeCache; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.graphics.Bitmap; import android.os.Binder; import android.os.Debug; +import android.os.Handler; import android.os.IBinder; -import android.os.Message; +import android.os.Looper; +import android.os.Trace; import android.util.Slog; import android.view.IApplicationToken; +import android.view.WindowManagerPolicy.StartingSurface; +import com.android.server.AttributeCache; /** * Controller for the app window token container. This is created by activity manager to link * activity records to the app window token container they use in window manager. @@ -52,6 +52,7 @@ public class AppWindowContainerController extends WindowContainerController<AppWindowToken, AppWindowContainerListener> { private final IApplicationToken mToken; + private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Runnable mOnWindowsDrawn = () -> { if (mListener == null) { @@ -80,6 +81,94 @@ public class AppWindowContainerController mListener.onWindowsGone(); }; + private final Runnable mAddStartingWindow = () -> { + final StartingData startingData; + final Configuration mergedOverrideConfiguration; + + synchronized (mWindowMap) { + startingData = mContainer.startingData; + mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration(); + } + + if (startingData == null) { + // Animation has been canceled... do nothing. + return; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting " + + this + ": pkg=" + mContainer.startingData.pkg); + + StartingSurface contents = null; + try { + contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg, + startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel, + startingData.labelRes, startingData.icon, startingData.logo, + startingData.windowFlags, mergedOverrideConfiguration); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when adding starting window", e); + } + if (contents != null) { + boolean abort = false; + + synchronized(mWindowMap) { + if (mContainer.removed || mContainer.startingData == null) { + // If the window was successfully added, then + // we need to remove it. + if (mContainer.startingWindow != null) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, + "Aborted starting " + mContainer + + ": removed=" + mContainer.removed + + " startingData=" + mContainer.startingData); + mContainer.startingWindow = null; + mContainer.startingData = null; + abort = true; + } + } else { + mContainer.startingSurface = contents; + } + if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM, + "Added starting " + mContainer + + ": startingWindow=" + + mContainer.startingWindow + " startingView=" + + mContainer.startingSurface); + } + + if (abort) { + try { + mService.mPolicy.removeSplashScreen(mContainer.token, contents); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when removing starting window", e); + } + } + } + }; + + private final Runnable mRemoveStartingWindow = () -> { + IBinder token = null; + StartingSurface contents = null; + synchronized (mWindowMap) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + + mContainer + ": startingWindow=" + + mContainer.startingWindow + " startingView=" + + mContainer.startingSurface); + if (mContainer.startingWindow != null) { + contents = mContainer.startingSurface; + token = mContainer.token; + mContainer.startingData = null; + mContainer.startingSurface = null; + mContainer.startingWindow = null; + mContainer.startingDisplayed = false; + } + } + if (contents != null) { + try { + mService.mPolicy.removeSplashScreen(token, contents); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when removing starting window", e); + } + } + }; + public AppWindowContainerController(IApplicationToken token, AppWindowContainerListener listener, int taskId, int index, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges, @@ -393,19 +482,42 @@ public class AppWindowContainerController if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData"); mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags); - final Message m = mService.mH.obtainMessage(ADD_STARTING, mContainer); - // Note: we really want to do sendMessageAtFrontOfQueue() because we - // want to process the message ASAP, before any other queued - // messages. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING"); - mService.mH.sendMessageAtFrontOfQueue(m); + scheduleAddStartingWindow(); } return true; } + void scheduleAddStartingWindow() { + + // Note: we really want to do sendMessageAtFrontOfQueue() because we + // want to process the message ASAP, before any other queued + // messages. + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING"); + mHandler.postAtFrontOfQueue(mAddStartingWindow); + } + public void removeStartingWindow() { synchronized (mWindowMap) { - mService.scheduleRemoveStartingWindowLocked(mContainer); + if (mHandler.hasCallbacks(mRemoveStartingWindow)) { + // Already scheduled. + return; + } + + if (mContainer.startingWindow == null) { + if (mContainer.startingData != null) { + // Starting window has not been added yet, but it is scheduled to be added. + // Go ahead and cancel the request. + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, + "Clearing startingData for token=" + mContainer); + mContainer.startingData = null; + } + return; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1) + + ": Schedule remove starting " + mContainer + + " startingWindow=" + mContainer.startingWindow); + mHandler.post(mRemoveStartingWindow); } } @@ -508,15 +620,15 @@ public class AppWindowContainerController void reportWindowsDrawn() { - mService.mH.post(mOnWindowsDrawn); + mHandler.post(mOnWindowsDrawn); } void reportWindowsVisible() { - mService.mH.post(mOnWindowsVisible); + mHandler.post(mOnWindowsVisible); } void reportWindowsGone() { - mService.mH.post(mOnWindowsGone); + mHandler.post(mOnWindowsGone); } /** Calls directly into activity manager so window manager lock shouldn't held. */ diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 0a4875810aba..f4fa2206a22a 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -29,9 +29,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; @@ -47,22 +47,21 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.logWithStack; -import android.os.Debug; -import com.android.internal.util.ToBooleanFunction; -import com.android.server.input.InputApplicationHandle; -import com.android.server.wm.WindowManagerService.H; - import android.annotation.NonNull; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; +import android.os.Debug; import android.os.IBinder; -import android.os.Message; import android.os.SystemClock; import android.util.Slog; import android.view.IApplicationToken; -import android.view.View; import android.view.WindowManager; +import android.view.WindowManagerPolicy.StartingSurface; + +import com.android.internal.util.ToBooleanFunction; +import com.android.server.input.InputApplicationHandle; +import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayDeque; @@ -138,7 +137,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // Information about an application starting window if displayed. StartingData startingData; WindowState startingWindow; - View startingView; + StartingSurface startingSurface; boolean startingDisplayed; boolean startingMoved; boolean firstWindowDrawn; @@ -213,8 +212,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // it from behind the starting window, so there is no need for it to also be doing its // own stuff. winAnimator.clearAnimation(); - winAnimator.mService.mFinishedStarting.add(this); - winAnimator.mService.mH.sendEmptyMessage(H.FINISHED_STARTING); + if (getController() != null) { + getController().removeStartingWindow(); + } } updateReportedVisibilityLocked(); } @@ -439,8 +439,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } void onRemovedFromDisplay() { - AppWindowToken startingToken = null; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this); boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); @@ -461,6 +459,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); + if (startingData != null && getController() != null) { + getController().removeStartingWindow(); + } + final TaskStack stack = mTask.mStack; if (delayed && !isEmpty()) { // set the token aside because it has an active animation to be finished @@ -477,9 +479,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } removed = true; - if (startingData != null) { - startingToken = this; - } stopFreezingScreen(true, true); if (mService.mFocusedApp == this) { if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this); @@ -491,9 +490,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (!delayed) { updateReportedVisibilityLocked(); } - - // Will only remove if startingToken non null. - mService.scheduleRemoveStartingWindowLocked(startingToken); } void clearAnimatingFlags() { @@ -557,7 +553,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mAppStopped = true; destroySurfaces(); // Remove any starting window that was added for this app if they are still around. - mTask.mService.scheduleRemoveStartingWindowLocked(this); + if (getController() != null) { + getController().removeStartingWindow(); + } } /** @@ -667,16 +665,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // TODO: Something smells about the code below...Is there a better way? if (startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win); - mService.scheduleRemoveStartingWindowLocked(this); + if (getController() != null) { + getController().removeStartingWindow(); + } } else if (mChildren.size() == 0 && startingData != null) { // If this is the last window and we had requested a starting transition window, // well there is no point now. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow"); startingData = null; - } else if (mChildren.size() == 1 && startingView != null) { + } else if (mChildren.size() == 1 && startingSurface != null) { // If this is the last window except for a starting transition window, // we need to get rid of the starting transition. - mService.scheduleRemoveStartingWindowLocked(this); + if (getController() != null) { + getController().removeStartingWindow(); + } } } @@ -1015,7 +1017,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } final WindowState tStartingWindow = fromToken.startingWindow; - if (tStartingWindow != null && fromToken.startingView != null) { + if (tStartingWindow != null && fromToken.startingSurface != null) { // In this case, the starting icon has already been displayed, so start // letting windows get shown immediately without any more transitions. mService.mSkipAppTransitionAnimation = true; @@ -1027,13 +1029,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // Transfer the starting window over to the new token. startingData = fromToken.startingData; - startingView = fromToken.startingView; + startingSurface = fromToken.startingSurface; startingDisplayed = fromToken.startingDisplayed; fromToken.startingDisplayed = false; startingWindow = tStartingWindow; reportedVisible = fromToken.reportedVisible; fromToken.startingData = null; - fromToken.startingView = null; + fromToken.startingSurface = null; fromToken.startingWindow = null; fromToken.startingMoved = true; tStartingWindow.mToken = this; @@ -1080,10 +1082,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree startingData = fromToken.startingData; fromToken.startingData = null; fromToken.startingMoved = true; - final Message m = mService.mH.obtainMessage(H.ADD_STARTING, this); - // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the - // message ASAP, before any other queued messages. - mService.mH.sendMessageAtFrontOfQueue(m); + if (getController() != null) { + getController().scheduleAddStartingWindow(); + } return true; } @@ -1421,10 +1422,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); pw.print(" mIsExiting="); pw.println(mIsExiting); } - if (startingWindow != null || startingView != null + if (startingWindow != null || startingSurface != null || startingDisplayed || startingMoved) { pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); - pw.print(" startingView="); pw.print(startingView); + pw.print(" startingSurface="); pw.print(startingSurface); pw.print(" startingDisplayed="); pw.print(startingDisplayed); pw.print(" startingMoved="); pw.println(startingMoved); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index dc06d129c5bb..6ac172b26e24 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -322,7 +322,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (!configChanged) { return null; } - displayContent.onOverrideConfigurationChanged(currentConfig); + displayContent.onOverrideConfigurationChanged(newConfiguration); if (displayId == DEFAULT_DISPLAY) { // Override configuration of the default display duplicates global config. In this case @@ -498,7 +498,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin, "RECOVER DESTROY", false); winAnimator.destroySurface(); - mService.scheduleRemoveStartingWindowLocked(winAnimator.mWin.mAppToken); + if (winAnimator.mWin.mAppToken != null + && winAnimator.mWin.mAppToken.getController() != null) { + winAnimator.mWin.mAppToken.getController().removeStartingWindow(); + } } try { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 38cb54320a1b..4e259354ae2a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2851,33 +2851,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - void scheduleRemoveStartingWindowLocked(AppWindowToken wtoken) { - if (wtoken == null) { - return; - } - if (mH.hasMessages(H.REMOVE_STARTING, wtoken)) { - // Already scheduled. - return; - } - - if (wtoken.startingWindow == null) { - if (wtoken.startingData != null) { - // Starting window has not been added yet, but it is scheduled to be added. - // Go ahead and cancel the request. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Clearing startingData for token=" + wtoken); - wtoken.startingData = null; - } - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, Debug.getCallers(1) + - ": Schedule remove starting " + wtoken + (wtoken != null ? - " startingWindow=" + wtoken.startingWindow : "")); - Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken); - mH.sendMessage(m); - } - public void moveTaskToTop(int taskId) { final long origId = Binder.clearCallingIdentity(); try { @@ -5578,9 +5551,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int REPORT_FOCUS_CHANGE = 2; public static final int REPORT_LOSING_FOCUS = 3; public static final int DO_TRAVERSAL = 4; - public static final int ADD_STARTING = 5; - public static final int REMOVE_STARTING = 6; - public static final int FINISHED_STARTING = 7; public static final int WINDOW_FREEZE_TIMEOUT = 11; public static final int APP_TRANSITION_TIMEOUT = 13; @@ -5722,126 +5692,6 @@ public class WindowManagerService extends IWindowManager.Stub } } break; - case ADD_STARTING: { - final AppWindowToken wtoken = (AppWindowToken)msg.obj; - final StartingData sd = wtoken.startingData; - - if (sd == null) { - // Animation has been canceled... do nothing. - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting " - + wtoken + ": pkg=" + sd.pkg); - - View view = null; - try { - view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme, - sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo, - sd.windowFlags, wtoken.getMergedOverrideConfiguration()); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when adding starting window", e); - } - - if (view != null) { - boolean abort = false; - - synchronized(mWindowMap) { - if (wtoken.removed || wtoken.startingData == null) { - // If the window was successfully added, then - // we need to remove it. - if (wtoken.startingWindow != null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Aborted starting " + wtoken - + ": removed=" + wtoken.removed - + " startingData=" + wtoken.startingData); - wtoken.startingWindow = null; - wtoken.startingData = null; - abort = true; - } - } else { - wtoken.startingView = view; - } - if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM, - "Added starting " + wtoken - + ": startingWindow=" - + wtoken.startingWindow + " startingView=" - + wtoken.startingView); - } - - if (abort) { - try { - mPolicy.removeStartingWindow(wtoken.token, view); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - } - } - } break; - - case REMOVE_STARTING: { - final AppWindowToken wtoken = (AppWindowToken)msg.obj; - IBinder token = null; - View view = null; - synchronized (mWindowMap) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " - + wtoken + ": startingWindow=" - + wtoken.startingWindow + " startingView=" - + wtoken.startingView); - if (wtoken.startingWindow != null) { - view = wtoken.startingView; - token = wtoken.token; - wtoken.startingData = null; - wtoken.startingView = null; - wtoken.startingWindow = null; - wtoken.startingDisplayed = false; - } - } - if (view != null) { - try { - mPolicy.removeStartingWindow(token, view); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - } - } break; - - case FINISHED_STARTING: { - IBinder token = null; - View view = null; - while (true) { - synchronized (mWindowMap) { - final int N = mFinishedStarting.size(); - if (N <= 0) { - break; - } - AppWindowToken wtoken = mFinishedStarting.remove(N-1); - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Finished starting " + wtoken - + ": startingWindow=" + wtoken.startingWindow - + " startingView=" + wtoken.startingView); - - if (wtoken.startingWindow == null) { - continue; - } - - view = wtoken.startingView; - token = wtoken.token; - wtoken.startingData = null; - wtoken.startingView = null; - wtoken.startingWindow = null; - wtoken.startingDisplayed = false; - } - - try { - mPolicy.removeStartingWindow(token, view); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - } - } break; - case WINDOW_FREEZE_TIMEOUT: { // TODO(multidisplay): Can non-default displays rotate? synchronized (mWindowMap) { @@ -7348,21 +7198,6 @@ public class WindowManagerService extends IWindowManager.Stub private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) { pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)"); mRoot.dumpTokens(pw, dumpAll); - if (!mFinishedStarting.isEmpty()) { - pw.println(); - pw.println(" Finishing start of application tokens:"); - for (int i=mFinishedStarting.size()-1; i>=0; i--) { - WindowToken token = mFinishedStarting.get(i); - pw.print(" Finished Starting #"); pw.print(i); - pw.print(' '); pw.print(token); - if (dumpAll) { - pw.println(':'); - token.dump(pw, " "); - } else { - pw.println(); - } - } - } if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty()) { pw.println(); if (mOpeningApps.size() > 0) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index e5ed18d118ad..19ef44cace33 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -461,16 +461,7 @@ class WindowStateAnimator { mStackClip = STACK_CLIP_BEFORE_ANIM; mWin.checkPolicyVisibilityChange(); mTransformation.clear(); - if (mDrawState == HAS_DRAWN - && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING - && mWin.mAppToken != null - && mWin.mAppToken.firstWindowDrawn - && mWin.mAppToken.startingData != null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Finish starting " - + mWin.mToken + ": first real window done animating"); - mService.mFinishedStarting.add(mWin.mAppToken); - mService.mH.sendEmptyMessage(H.FINISHED_STARTING); - } else if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) { + if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) { // Upon completion of a not-visible to visible status bar animation a relayout is // required. if (displayContent != null) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 1aabd5ea66f9..4df100105bcb 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -441,8 +441,9 @@ class WindowSurfacePlacer { wtoken.deferClearAllDrawn = false; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) { - mService.scheduleRemoveStartingWindowLocked(wtoken); + if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit + && wtoken.getController() != null) { + wtoken.getController().removeStartingWindow(); } mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp index c54d732b9285..4d85d9a558aa 100644 --- a/services/core/jni/com_android_server_connectivity_Vpn.cpp +++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp @@ -17,24 +17,25 @@ #define LOG_NDEBUG 0 #define LOG_TAG "VpnJni" -#include <cutils/log.h> -#include "netutils/ifc.h" -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> - #include <linux/if.h> #include <linux/if_tun.h> #include <linux/route.h> #include <linux/ipv6_route.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <log/log.h> + +#include "netutils/ifc.h" #include "jni.h" #include "JNIHelp.h" diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp index cf7f1cbfd0be..2fd0603c8f89 100644 --- a/services/core/jni/com_android_server_lights_LightsService.cpp +++ b/services/core/jni/com_android_server_lights_LightsService.cpp @@ -103,16 +103,15 @@ static void setLight_native( state.flashMode = Flash::NONE; } else { // Only set non-brightness settings when not in low-persistence mode - state.color = colorARGB; state.flashMode = flash; state.flashOnMs = onMS; state.flashOffMs = offMS; } + state.color = colorARGB; state.brightnessMode = brightness; Status status; - { ALOGD_IF_SLOW(50, "Excessive delay setting light"); Return<Status> ret = gLight->setLight(type, state); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 09886db17af6..fa6405a1dd87 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -59,6 +59,12 @@ static jmethodID method_reportGeofenceResumeStatus; static jmethodID method_reportMeasurementData; static jmethodID method_reportNavigationMessages; +/* + * Save a pointer to JavaVm to attach/detach threads executing + * callback methods that need to make JNI calls. + */ +static JavaVM* sJvm; + using android::OK; using android::sp; using android::status_t; @@ -216,6 +222,62 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } } +class ScopedJniThreadAttach { +public: + ScopedJniThreadAttach() { + /* + * attachResult will also be JNI_OK if the thead was already attached to + * JNI before the call to AttachCurrentThread(). + */ + jint attachResult = sJvm->AttachCurrentThread(&mEnv, nullptr); + LOG_ALWAYS_FATAL_IF(attachResult != JNI_OK, "Unable to attach thread. Error %d", + attachResult); + } + + ~ScopedJniThreadAttach() { + jint detachResult = sJvm->DetachCurrentThread(); + /* + * Return if the thread was already detached. Log error for any other + * failure. + */ + if (detachResult == JNI_EDETACHED) { + return; + } + + LOG_ALWAYS_FATAL_IF(detachResult != JNI_OK, "Unable to detach thread. Error %d", + detachResult); + } + + JNIEnv* getEnv() { + /* + * Checking validity of mEnv in case the thread was detached elsewhere. + */ + LOG_ALWAYS_FATAL_IF(AndroidRuntime::getJNIEnv() != mEnv); + return mEnv; + } + +private: + JNIEnv* mEnv = nullptr; +}; + +thread_local std::unique_ptr<ScopedJniThreadAttach> tJniThreadAttacher; + +static JNIEnv* getJniEnv() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + /* + * If env is nullptr, the thread is not already attached to + * JNI. It is attached below and the destructor for ScopedJniThreadAttach + * will detach it on thread exit. + */ + if (env == nullptr) { + tJniThreadAttacher.reset(new ScopedJniThreadAttach()); + env = tJniThreadAttacher->getEnv(); + } + + return env; +} + /* * GnssCallback class implements the callback methods for IGnss interface. */ @@ -247,7 +309,7 @@ size_t GnssCallback::sGnssSvListSize = 0; Return<void> GnssCallback::gnssLocationCb( const ::android::hardware::gnss::V1_0::GnssLocation& location) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_reportLocation, location.gnssLocationFlags, @@ -263,14 +325,14 @@ Return<void> GnssCallback::gnssLocationCb( } Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); } Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); sGnssSvListSize = svStatus.numSvs; if (sGnssSvListSize > static_cast<uint32_t>( @@ -292,7 +354,7 @@ Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svS Return<void> GnssCallback::gnssNmeaCb( int64_t timestamp, const ::android::hardware::hidl_string& nmea) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); /* * The Java code will call back to read these values. * We do this to avoid creating unnecessary String objects. @@ -308,7 +370,7 @@ Return<void> GnssCallback::gnssNmeaCb( Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) { ALOGD("%s: %du\n", __func__, capabilities); - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); @@ -325,7 +387,7 @@ Return<void> GnssCallback::gnssReleaseWakelockCb() { } Return<void> GnssCallback::gnssRequestTimeCb() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); @@ -334,7 +396,7 @@ Return<void> GnssCallback::gnssRequestTimeCb() { Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) { ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw); - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -350,7 +412,7 @@ class GnssXtraCallback : public IGnssXtraCallback { * interface. */ Return<void> GnssXtraCallback::downloadRequestCb() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); @@ -385,7 +447,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb( const android::hardware::gnss::V1_0::GnssLocation& location, GeofenceTransition transition, hardware::gnss::V1_0::GnssUtcTime timestamp) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, @@ -408,7 +470,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb( Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb( GeofenceAvailability status, const android::hardware::gnss::V1_0::GnssLocation& location) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status, @@ -426,7 +488,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb( Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) { ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status); } @@ -441,7 +503,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId, Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) { ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status); } @@ -455,7 +517,7 @@ Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId, Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) { ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status); } @@ -469,7 +531,7 @@ Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId, Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) { ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status); } @@ -496,7 +558,7 @@ struct GnssNavigationMessageCallback : public IGnssNavigationMessageCallback { Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( const IGnssNavigationMessageCallback::GnssNavigationMessage& message) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); size_t dataLength = message.data.size(); @@ -545,7 +607,7 @@ struct GnssMeasurementCallback : public IGnssMeasurementCallback { Return<void> GnssMeasurementCallback::GnssMeasurementCb( const IGnssMeasurementCallback::GnssData& data) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); jobject clock; jobjectArray measurementArray; @@ -700,7 +762,7 @@ struct GnssNiCallback : public IGnssNiCallback { Return<void> GnssNiCallback::niNotifyCb( const IGnssNiCallback::GnssNiNotification& notification) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); jstring requestorId = env->NewStringUTF(notification.requestorId.c_str()); jstring text = env->NewStringUTF(notification.notificationMessage.c_str()); @@ -742,7 +804,7 @@ struct AGnssCallback : public IAGnssCallback { Return<void> AGnssCallback::agnssStatusIpV6Cb( const IAGnssCallback::AGnssStatusIpV6& agps_status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); jbyteArray byteArray = NULL; bool isSupported = false; @@ -778,7 +840,7 @@ Return<void> AGnssCallback::agnssStatusIpV6Cb( Return<void> AGnssCallback::agnssStatusIpV4Cb( const IAGnssCallback::AGnssStatusIpV4& agps_status) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); jbyteArray byteArray = NULL; uint32_t ipAddr = agps_status.ipV4Addr; @@ -813,7 +875,7 @@ jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) { return NULL; } - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); jbyteArray byteArray = env->NewByteArray(4); if (byteArray == NULL) { ALOGE("Unable to allocate byte array for IPv4 address"); @@ -832,19 +894,19 @@ jbyteArray AGnssCallback::convertToIpV4(uint32_t ip) { * interface. */ struct AGnssRilCallback : IAGnssRilCallback { - Return<void> requestSetIdCb(IAGnssRilCallback::ID setIdFlag) override; + Return<void> requestSetIdCb(uint32_t setIdFlag) override; Return<void> requestRefLocCb() override; }; -Return<void> AGnssRilCallback::requestSetIdCb(IAGnssRilCallback::ID setIdFlag) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); +Return<void> AGnssRilCallback::requestSetIdCb(uint32_t setIdFlag) { + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); } Return<void> AGnssRilCallback::requestRefLocCb() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); + JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_requestRefLocation); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); @@ -885,6 +947,14 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, "reportNavigationMessage", "(Landroid/location/GnssNavigationMessage;)V"); + /* + * Save a pointer to JVM. + */ + jint jvmStatus = env->GetJavaVM(&sJvm); + if (jvmStatus != JNI_OK) { + LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); + } + // TODO(b/31632518) gnssHal = IGnss::getService("gnss"); if (gnssHal != nullptr) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9f6606278cec..2e5b6876719e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -10097,6 +10097,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { .setSmallIcon(R.drawable.ic_qs_network_logging) .setContentTitle(mContext.getString(R.string.network_logging_notification_title)) .setContentText(mContext.getString(R.string.network_logging_notification_text)) + .setTicker(mContext.getString(R.string.network_logging_notification_title)) .setShowWhen(true) .setContentIntent(pendingIntent) .build(); diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index af570b0dff08..1189dae7b21c 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -23,6 +23,7 @@ import static android.net.NetworkScoreManager.CACHE_FILTER_NONE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -61,6 +62,7 @@ import android.net.RecommendationResult; import android.net.ScoredNetwork; import android.net.WifiKey; import android.net.wifi.WifiConfiguration; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; @@ -261,7 +263,7 @@ public class NetworkScoreServiceTest { @Test public void testUpdateScores_notActiveScorer() { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false); + bindToScorer(false /*callerIsScorer*/); try { mNetworkScoreService.updateScores(new ScoredNetwork[0]); @@ -273,7 +275,7 @@ public class NetworkScoreServiceTest { @Test public void testUpdateScores_oneRegisteredCache() throws RemoteException { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true); + bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); @@ -288,7 +290,7 @@ public class NetworkScoreServiceTest { @Test public void testUpdateScores_twoRegisteredCaches() throws RemoteException { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true); + bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); @@ -323,7 +325,7 @@ public class NetworkScoreServiceTest { @Test public void testClearScores_notActiveScorer_noRequestNetworkScoresPermission() { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false); + bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); try { @@ -336,7 +338,7 @@ public class NetworkScoreServiceTest { @Test public void testClearScores_activeScorer_noRequestNetworkScoresPermission() { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true); + bindToScorer(true /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); @@ -345,7 +347,7 @@ public class NetworkScoreServiceTest { @Test public void testClearScores_activeScorer() throws RemoteException { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(true); + bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); @@ -357,7 +359,7 @@ public class NetworkScoreServiceTest { @Test public void testClearScores_notActiveScorer_hasRequestNetworkScoresPermission() throws RemoteException { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false); + bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -383,7 +385,7 @@ public class NetworkScoreServiceTest { @Test public void testDisableScoring_notActiveScorer_noRequestNetworkScoresPermission() { - when(mNetworkScorerAppManager.isCallerActiveScorer(anyInt())).thenReturn(false); + bindToScorer(false /*callerIsScorer*/); when(mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES)) .thenReturn(PackageManager.PERMISSION_DENIED); @@ -448,6 +450,42 @@ public class NetworkScoreServiceTest { assertFalse(stringWriter.toString().isEmpty()); } + @Test + public void testIsCallerActiveScorer_noBoundService() throws Exception { + mNetworkScoreService.systemRunning(); + + assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); + } + + @Test + public void testIsCallerActiveScorer_boundServiceIsNotCaller() throws Exception { + bindToScorer(false /*callerIsScorer*/); + + assertFalse(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); + } + + @Test + public void testIsCallerActiveScorer_boundServiceIsCaller() throws Exception { + bindToScorer(true /*callerIsScorer*/); + + assertTrue(mNetworkScoreService.isCallerActiveScorer(Binder.getCallingUid())); + } + + @Test + public void testGetActiveScorerPackage_notActive() throws Exception { + mNetworkScoreService.systemRunning(); + + assertNull(mNetworkScoreService.getActiveScorerPackage()); + } + + @Test + public void testGetActiveScorerPackage_active() throws Exception { + when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); + mNetworkScoreService.systemRunning(); + + assertEquals(NEW_SCORER.packageName, mNetworkScoreService.getActiveScorerPackage()); + } + // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService. private void injectProvider() { final ComponentName componentName = new ComponentName(NEW_SCORER.packageName, @@ -467,4 +505,14 @@ public class NetworkScoreServiceTest { }); mNetworkScoreService.systemRunning(); } + + private void bindToScorer(boolean callerIsScorer) { + final int callingUid = callerIsScorer ? Binder.getCallingUid() : 0; + NetworkScorerAppData appData = new NetworkScorerAppData(NEW_SCORER.packageName, + callingUid, NEW_SCORER.recommendationServiceClassName); + when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(appData); + when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(), + isA(UserHandle.class))).thenReturn(true); + mNetworkScoreService.systemRunning(); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 10ca90207d85..228b8e0d82fb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -1403,7 +1403,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity, Icon icon, Intent intent, int rank) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) - .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy")) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) .setShortLabel(title) .setRank(rank) .setIntent(intent); @@ -1432,7 +1432,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity, Icon icon, Intent[] intents, int rank) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) - .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy")) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) .setShortLabel(title) .setRank(rank) .setIntents(intents); @@ -1455,7 +1455,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected ShortcutInfo makeShortcutWithExtras(String id, Intent intent, PersistableBundle extras) { final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) - .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy")) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) .setShortLabel("title-" + id) .setExtras(extras) .setIntent(intent); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java index fbf0ed2938eb..bcd72fcd6181 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java @@ -38,6 +38,7 @@ import android.graphics.drawable.Icon; import android.os.UserHandle; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; import android.util.Pair; import com.android.frameworks.servicestests.R; @@ -227,8 +228,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { private void assertPinItemRequest(PinItemRequest actualRequest) { assertNotNull(actualRequest); - assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, actualRequest.getRequestType()); + + Log.i(TAG, "Requested shortcut: " + actualRequest.getShortcutInfo().toInsecureString()); } /** @@ -243,9 +245,16 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32); runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { - ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32); + /// Create a shortcut with no target activity. + final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, "s1") + .setShortLabel("Title-" + "s1") + .setIcon(res32x32) + .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class)); + final ShortcutInfo s = b.build(); + + assertNull(s.getActivity()); - assertTrue(mManager.requestPinShortcut(s1, + assertTrue(mManager.requestPinShortcut(s, resultIntent == null ? null : resultIntent.getIntentSender())); verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class)); @@ -271,6 +280,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(request.getShortcutInfo()) .haveIds("s1") .areAllOrphan() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS)) .areAllWithNoIntent(); assertAllHaveIcon(list(request.getShortcutInfo())); @@ -295,6 +305,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .areAllNotDynamic() .areAllEnabled() .areAllPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, MAIN_ACTIVITY_CLASS)) .areAllWithIntent(); }); } @@ -310,6 +321,145 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { checkRequestPinShortcut(resultIntent); } + public void testRequestPinShortcut_explicitTargetActivity() { + setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0)); + setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10)); + + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + ShortcutInfo s1 = makeShortcutWithActivity("s1", + new ComponentName(CALLING_PACKAGE_1, "different_activity")); + + assertTrue(mManager.requestPinShortcut(s1, null)); + + verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class)); + + // Shortcut shouldn't be registered yet. + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + // Check the intent passed to startActivityAsUser(). + final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); + + verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0)); + + assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage); + + // Check the request object. + final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue()); + + assertPinItemRequest(request); + + assertWith(request.getShortcutInfo()) + .haveIds("s1") + .areAllOrphan() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity")) + .areAllWithNoIntent(); + + // Accept the request. + assertForLauncherCallbackNoThrow(mLauncherApps, + () -> assertTrue(request.accept())) + .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0) + .haveIds("s1"); + }); + + verify(mServiceContext, times(1)).sendIntentSender(eq(null)); + + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("s1") + .areAllNotDynamic() + .areAllEnabled() + .areAllPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "different_activity")) + .areAllWithIntent(); + }); + } + + public void testRequestPinShortcut_wrongTargetActivity() { + setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0)); + + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + // Create dynamic shortcut + ShortcutInfo s1 = makeShortcutWithActivity("s1", + new ComponentName("wrong_package", "different_activity")); + + assertExpectException(IllegalStateException.class, "not belong to package", () -> { + assertTrue(mManager.requestPinShortcut(s1, /* resultIntent=*/ null)); + }); + + verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class)); + verify(mServiceContext, times(0)).startActivityAsUser( + any(Intent.class), any(UserHandle.class)); + }); + } + + public void testRequestPinShortcut_noTargetActivity_noMainActivity() { + setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0)); + setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10)); + + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + /// Create a shortcut with no target activity. + final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, "s1") + .setShortLabel("Title-" + "s1") + .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class)); + final ShortcutInfo s = b.build(); + + assertNull(s.getActivity()); + + // Caller has no main activity. + mMainActivityFetcher = (packageName, userId) -> null; + + assertTrue(mManager.requestPinShortcut(s, null)); + + verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class)); + + // Shortcut shouldn't be registered yet. + assertWith(getCallerShortcuts()) + .isEmpty(); + }); + + runWithCaller(LAUNCHER_1, USER_0, () -> { + // Check the intent passed to startActivityAsUser(). + final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); + + verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0)); + + assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage); + + // Check the request object. + final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue()); + + assertPinItemRequest(request); + + assertWith(request.getShortcutInfo()) + .haveIds("s1") + .areAllOrphan() + .areAllWithNoActivity() // Activity is not set; expected. + .areAllWithNoIntent(); + + // Accept the request. + assertForLauncherCallbackNoThrow(mLauncherApps, + () -> assertTrue(request.accept())) + .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0) + .haveIds("s1"); + }); + + verify(mServiceContext, times(1)).sendIntentSender(eq(null)); + + runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { + assertWith(getCallerShortcuts()) + .haveIds("s1") + .areAllNotDynamic() + .areAllEnabled() + .areAllPinned() + .areAllWithNoActivity() // Activity is not set; expected. + .areAllWithIntent(); + }); + + } + public void testRequestPinShortcut_dynamicExists() { setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0)); @@ -328,6 +478,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("s1") .areAllDynamic() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllNotPinned(); }); @@ -348,6 +499,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); assertAllHaveIcon(list(request.getShortcutInfo())); @@ -361,6 +513,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); }); } @@ -379,6 +532,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("ms1") .areAllManifest() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllNotPinned(); }); @@ -399,6 +554,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); assertAllHaveIcon(list(request.getShortcutInfo())); @@ -412,6 +569,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllPinned(); }); } @@ -431,6 +590,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("s1") .areAllDynamic() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"), @@ -456,6 +616,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("ms1") .areAllManifest() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllPinned(); assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"), @@ -483,6 +645,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllNotDynamic() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"), @@ -511,6 +674,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllNotDynamic() .areAllDisabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> { @@ -541,6 +705,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllNotManifest() .areAllDisabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllPinned(); assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> { @@ -570,6 +736,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("s1") .areAllDynamic() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); // The shortcut is already pinned, but not by the current launcher, so it'll still @@ -597,6 +764,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() // Note it's not pinned by this launcher. + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); // Accept the request. @@ -608,6 +776,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); }); } @@ -629,6 +798,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("ms1") .areAllManifest() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllPinned(); // The shortcut is already pinned, but not by the current launcher, so it'll still @@ -656,6 +827,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() // Note it's not pinned by this launcher. + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); // Accept the request. @@ -667,6 +840,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllPinned(); }); } @@ -710,6 +885,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); // Accept the request. @@ -719,6 +895,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1", "s2") .areAllDynamic() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); }); @@ -727,6 +904,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1", "s2") .areAllDynamic() .areAllEnabled() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllPinned(); }); } @@ -752,6 +930,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("s1") .areAllDynamic() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllNotPinned(); }); @@ -772,6 +951,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); assertAllHaveIcon(list(request.getShortcutInfo())); @@ -786,6 +966,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .areAllDynamic() .areAllEnabled() .areAllPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .forShortcutWithId("s1", (si) -> { // Still the original title. assertEquals("Title-s1", si.getShortLabel()); @@ -810,6 +991,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { assertWith(getCallerShortcuts()) .haveIds("ms1") .areAllManifest() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllNotPinned(); }); @@ -830,6 +1013,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); assertAllHaveIcon(list(request.getShortcutInfo())); @@ -844,6 +1029,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .areAllManifest() .areAllEnabled() .areAllPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .forShortcutWithId("ms1", (si) -> { // Still the original title. // Title should be something like: @@ -893,6 +1080,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); // Accept the request -> should fail. @@ -950,9 +1138,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); - // Accept the request -> should fail. assertTrue(request.accept()); }); @@ -1004,6 +1192,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); // Accept the request -> should fail. @@ -1059,10 +1249,11 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); - // Accept the request -> should fail. assertTrue(request.accept()); }); @@ -1071,6 +1262,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllMutable() // Note it's no longer immutable. .areAllFloating() + + // Note it's the activity from makeShortcutWithShortLabel(). + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .forShortcutWithId("ms1", si -> { assertEquals("new", si.getShortLabel()); }); @@ -1104,6 +1298,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { mManager.disableShortcuts(list("s1")); assertWith(getCallerShortcuts()) .haveIds("s1") + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllDisabled(); }); @@ -1125,6 +1320,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("s1") .areAllDynamic() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllWithNoIntent(); // Accept the request -> should fail. @@ -1144,6 +1340,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { assertWith(getCallerShortcuts()) .haveIds("s1") + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main")) .areAllDisabled(); }); } @@ -1174,6 +1371,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { publishManifestShortcutsAsCaller(R.xml.shortcut_0); assertWith(getCallerShortcuts()) .haveIds("ms1") + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllDisabled(); }); @@ -1195,6 +1394,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { .haveIds("ms1") .areAllManifest() .areAllNotPinned() + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllWithNoIntent(); // Accept the request -> should fail. @@ -1214,6 +1415,8 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest { runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> { assertWith(getCallerShortcuts()) .haveIds("ms1") + .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, + ShortcutActivity.class.getName())) .areAllDisabled(); }); } diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 786c2bb06d96..85931e8ac878 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -19,6 +19,7 @@ package com.android.server.wm; import org.junit.Test; import org.junit.runner.RunWith; +import android.content.res.Configuration; import android.hardware.display.DisplayManagerGlobal; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; @@ -208,6 +209,10 @@ public class DisplayContentTests extends WindowTestsBase { voiceInteractionWindow.removeImmediately(); } + /** + * This tests stack movement between displays and proper stack's, task's and app token's display + * container references updates. + */ @Test public void testMoveStackBetweenDisplays() throws Exception { // Create second display. @@ -238,4 +243,28 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(sDisplayContent, task.getDisplayContent()); assertEquals(sDisplayContent, token.getDisplayContent()); } + + /** + * This tests override configuration updates for display content. + */ + @Test + public void testDisplayOverrideConfigUpdate() throws Exception { + final int displayId = sDisplayContent.getDisplayId(); + final Configuration currentOverrideConfig = sDisplayContent.getOverrideConfiguration(); + + // Create new, slightly changed override configuration and apply it to the display. + final Configuration newOverrideConfig = new Configuration(currentOverrideConfig); + newOverrideConfig.densityDpi += 120; + newOverrideConfig.fontScale += 0.3; + + sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, displayId); + + // Check that override config is applied. + assertEquals(newOverrideConfig, sDisplayContent.getOverrideConfiguration()); + + // Check that global configuration is updated, as we've updated default display's config. + final Configuration globalConfig = sWm.mRoot.getConfiguration(); + assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi); + assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 1853a653ed1a..c4fd7221d3f4 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -16,29 +16,6 @@ package com.android.server.wm; -import com.android.internal.policy.IKeyguardDismissCallback; -import com.android.internal.policy.IShortcutService; -import com.android.server.input.InputManagerService; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.view.Display; -import android.view.IWindowManager; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowManagerPolicy; -import android.view.animation.Animation; - -import java.io.PrintWriter; - import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; @@ -62,8 +39,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_POINTER; -import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; @@ -81,9 +58,30 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; - import static org.mockito.Mockito.mock; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Display; +import android.view.IWindowManager; +import android.view.KeyEvent; +import android.view.WindowManager; +import android.view.WindowManagerPolicy; +import android.view.animation.Animation; + +import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IShortcutService; +import com.android.server.input.InputManagerService; + +import java.io.PrintWriter; + class TestWindowManagerPolicy implements WindowManagerPolicy { private static final String TAG = "TestWindowManagerPolicy"; @@ -308,14 +306,14 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public View addStartingWindow(IBinder appToken, String packageName, int theme, + public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig) { return null; } @Override - public void removeStartingWindow(IBinder appToken, View window) { + public void removeSplashScreen(IBinder appToken, StartingSurface surface) { } diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index a664f21bc0dc..ea45bd17b216 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -925,7 +925,7 @@ public class ShortcutManagerTestUtils { } public ShortcutListAsserter areAllWithActivity(ComponentName activity) { - forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.getActivity().equals(activity))); + forAllShortcuts(s -> assertEquals("id=" + s.getId(), activity, s.getActivity())); return this; } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index c69b7c2771ac..b20384dfffbc 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -115,6 +115,13 @@ public final class Call { */ public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; + /** + * Extra key used to indicate the time (in millis) when the last outgoing emergency call was + * made. This is used to identify potential emergency callbacks. + */ + public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = + "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + public static class Details { /** Call can currently be put on hold or unheld. */ diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java index 85584d3ed2cc..4445a2238538 100644 --- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java +++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java @@ -51,4 +51,8 @@ public class AttachInfo_Accessor { view.dispatchDetachedFromWindow(); } } + + public static ViewRootImpl getRootView(View view) { + return view.mAttachInfo != null ? view.mAttachInfo.mViewRootImpl : null; + } } diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java new file mode 100644 index 000000000000..0e15b97243b0 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Accessor.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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.view; + +/** + * Accessor to allow layoutlib to call {@link ViewRootImpl#dispatchApplyInsets} directly. + */ +public class ViewRootImpl_Accessor { + public static void dispatchApplyInsets(ViewRootImpl viewRoot, View host) { + viewRoot.dispatchApplyInsets(host); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 726ff223bd8f..2fe3ed5dc5ee 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -38,7 +38,10 @@ import android.annotation.NonNull; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.AttachInfo_Accessor; import android.view.View; +import android.view.ViewRootImpl; +import android.view.ViewRootImpl_Accessor; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -302,6 +305,17 @@ class Layout extends RelativeLayout { return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name); } + @Override + public void requestFitSystemWindows() { + // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will + // act as view root for most purposes. That way, we can also save going through the Handler + // to dispatch the new applied insets. + ViewRootImpl root = AttachInfo_Accessor.getRootView(this); + if (root != null) { + ViewRootImpl_Accessor.dispatchApplyInsets(root, this); + } + } + /** * A helper class to help initialize the Layout. */ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java new file mode 100644 index 000000000000..36e5c2646420 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/InsetsWidget.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 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.test.myapplication.widgets; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.WindowInsets; +import android.widget.TextView; + +public class InsetsWidget extends TextView { + public static boolean sApplyInsetsCalled = false; + + public InsetsWidget(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + requestApplyInsets(); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + sApplyInsetsCalled = true; + return super.onApplyWindowInsets(insets); + } +} diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml new file mode 100644 index 000000000000..ff06d79dd00a --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/insets.xml @@ -0,0 +1,12 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:padding="16dp" + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <com.android.layoutlib.test.myapplication.widgets.InsetsWidget + android:text="Hello world" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/text1"/> +</LinearLayout> diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index c813a1292318..7a436eb3313e 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -63,6 +63,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -87,6 +88,7 @@ import java.util.zip.ZipFile; import com.google.android.collect.Lists; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -434,6 +436,37 @@ public class Main { renderAndVerify(params, "simple_activity.png"); } + @Test + public void testOnApplyInsetsCall() + throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { + // We get the widget via reflection to avoid IntelliJ complaining about the class being + // located in the wrong package. (From the Bridge tests point of view, it is) + Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" + + ".InsetsWidget"); + Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled"); + assertFalse((Boolean)field.get(null)); + + LayoutPullParser parser = createLayoutPullParser("insets.xml"); + LayoutLibTestCallback layoutLibCallback = + new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); + layoutLibCallback.initResources(); + SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, + layoutLibCallback, "Theme.Material.Light.NoActionBar", false, + RenderingMode.NORMAL, 22); + try { + renderAndVerify(params, "scrolled.png"); + } catch(AssertionError e) { + // In this particular test we do not care about the image similarity. + // TODO: Create new render method that allows to not compare images. + if (!e.getLocalizedMessage().startsWith("Images differ")) { + throw e; + } + } + + assertTrue((Boolean)field.get(null)); + field.set(null, false); + } + @AfterClass public static void tearDown() { sLayoutLibLog = null; |