Merge "Camera2: Temporarily remove surface size 0 check"
diff --git a/Android.mk b/Android.mk
index 22323c5..1da4783 100644
--- a/Android.mk
+++ b/Android.mk
@@ -265,8 +265,9 @@
core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.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/autofill/IFillCallback.aidl \
+ core/java/android/service/autofill/ISaveCallback.aidl \
core/java/android/service/carrier/ICarrierService.aidl \
core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
core/java/android/service/carrier/ICarrierMessagingService.aidl \
diff --git a/api/current.txt b/api/current.txt
index eca3c27..8ee6ef8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5046,14 +5046,19 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public int getBadgeIcon();
method public java.lang.String getChannel();
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
+ method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
+ field public static final int BADGE_ICON_LARGE = 2; // 0x2
+ field public static final int BADGE_ICON_NONE = 0; // 0x0
+ field public static final int BADGE_ICON_SMALL = 1; // 0x1
field public static final java.lang.String CATEGORY_ALARM = "alarm";
field public static final java.lang.String CATEGORY_CALL = "call";
field public static final java.lang.String CATEGORY_EMAIL = "email";
@@ -5145,7 +5150,7 @@
field public deprecated int ledARGB;
field public deprecated int ledOffMS;
field public deprecated int ledOnMS;
- field public deprecated int number;
+ field public int number;
field public deprecated int priority;
field public android.app.Notification publicVersion;
field public deprecated android.net.Uri sound;
@@ -5233,6 +5238,7 @@
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
method public android.app.Notification build();
+ method public android.app.Notification.Builder chooseBadgeIcon(int);
method public android.widget.RemoteViews createBigContentView();
method public android.widget.RemoteViews createContentView();
method public android.widget.RemoteViews createHeadsUpContentView();
@@ -5265,13 +5271,14 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public deprecated android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public deprecated android.app.Notification.Builder setNumber(int);
+ method public android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public deprecated android.app.Notification.Builder setPriority(int);
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
method public android.app.Notification.Builder setSmallIcon(int, int);
@@ -9092,6 +9099,10 @@
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+ field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+ field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -10327,6 +10338,7 @@
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+ field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch";
@@ -20443,6 +20455,7 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
+ method public static int getVolumeControlStream(android.media.AudioAttributes);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -23586,6 +23599,8 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
+ method public void addQueueItem(android.media.MediaDescription);
+ method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -23604,6 +23619,8 @@
method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
+ method public void removeQueueItem(android.media.MediaDescription);
+ method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -23681,11 +23698,14 @@
method public void setSessionActivity(android.app.PendingIntent);
method public void setShuffleModeEnabled(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+ field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
+ method public void onAddQueueItem(android.media.MediaDescription);
+ method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -23699,6 +23719,8 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+ method public void onRemoveQueueItem(android.media.MediaDescription);
+ method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
@@ -36096,26 +36118,19 @@
ctor public AutoFillService();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
- method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
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 void onFillResponseAuthenticationRequest(android.os.Bundle, int);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, 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 int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
- field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
- field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
- field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
- public final class FillCallback {
- method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+ public final class FillCallback implements android.os.Parcelable {
+ method public int describeContents();
method public void onFailure(java.lang.CharSequence);
- method public void onFillResponseAuthentication(int);
method public void onSuccess(android.view.autofill.FillResponse);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
}
public final class SaveCallback {
@@ -41952,6 +41967,15 @@
method public android.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
}
+ public abstract class TransitionListenerAdapter implements android.transition.Transition.TransitionListener {
+ ctor public TransitionListenerAdapter();
+ method public void onTransitionCancel(android.transition.Transition);
+ method public void onTransitionEnd(android.transition.Transition);
+ method public void onTransitionPause(android.transition.Transition);
+ method public void onTransitionResume(android.transition.Transition);
+ method public void onTransitionStart(android.transition.Transition);
+ }
+
public class TransitionManager {
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
@@ -44350,8 +44374,8 @@
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final <T extends android.view.View> T findViewById(int);
- method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
+ method public final android.view.View findViewById(int);
+ method public final android.view.View findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -46755,10 +46779,9 @@
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(java.lang.CharSequence);
+ ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
method public android.view.autofill.Dataset build();
- method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
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);
}
@@ -46770,12 +46793,11 @@
}
public static final class FillResponse.Builder {
- ctor public FillResponse.Builder();
+ ctor public FillResponse.Builder(java.lang.String);
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 requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
}
@@ -47201,7 +47223,7 @@
public final class TextClassificationManager {
method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
- method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+ method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
}
public final class TextClassificationResult {
@@ -50532,6 +50554,7 @@
method public void setIs24HourView(java.lang.Boolean);
method public void setMinute(int);
method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
+ method public boolean validateInput();
}
public static abstract interface TimePicker.OnTimeChangedListener {
@@ -50930,6 +50953,8 @@
field public static final int OP_INVOKE_INTERFACE = 114; // 0x72
field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff
field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78
+ field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa
+ field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb
field public static final int OP_INVOKE_STATIC = 113; // 0x71
field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff
field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77
@@ -54224,12 +54249,15 @@
}
public abstract class MethodHandle {
+ method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asFixedArity();
+ method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+ method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable;
method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
method public boolean isVarargsCollector();
method public java.lang.invoke.MethodType type();
@@ -54263,17 +54291,23 @@
method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+ method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...);
method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandles.Lookup lookup();
method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+ method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int);
method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
}
@@ -54290,6 +54324,7 @@
method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
+ method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 55b32f6..c4505bc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5206,15 +5206,20 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public int getBadgeIcon();
method public java.lang.String getChannel();
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
+ method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
+ field public static final int BADGE_ICON_LARGE = 2; // 0x2
+ field public static final int BADGE_ICON_NONE = 0; // 0x0
+ field public static final int BADGE_ICON_SMALL = 1; // 0x1
field public static final java.lang.String CATEGORY_ALARM = "alarm";
field public static final java.lang.String CATEGORY_CALL = "call";
field public static final java.lang.String CATEGORY_EMAIL = "email";
@@ -5308,7 +5313,7 @@
field public deprecated int ledARGB;
field public deprecated int ledOffMS;
field public deprecated int ledOnMS;
- field public deprecated int number;
+ field public int number;
field public deprecated int priority;
field public android.app.Notification publicVersion;
field public deprecated android.net.Uri sound;
@@ -5396,6 +5401,7 @@
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
method public android.app.Notification build();
+ method public android.app.Notification.Builder chooseBadgeIcon(int);
method public android.widget.RemoteViews createBigContentView();
method public android.widget.RemoteViews createContentView();
method public android.widget.RemoteViews createHeadsUpContentView();
@@ -5428,13 +5434,14 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public deprecated android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public deprecated android.app.Notification.Builder setNumber(int);
+ method public android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public deprecated android.app.Notification.Builder setPriority(int);
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
method public android.app.Notification.Builder setSmallIcon(int, int);
@@ -6204,6 +6211,10 @@
method public void onDetached();
}
+ public class VrManager {
+ method public void setPersistentVrModeEnabled(boolean);
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -9006,6 +9017,7 @@
field public static final java.lang.String USB_SERVICE = "usb";
field public static final java.lang.String USER_SERVICE = "user";
field public static final java.lang.String VIBRATOR_SERVICE = "vibrator";
+ field public static final java.lang.String VR_SERVICE = "vrmanager";
field public static final java.lang.String WALLPAPER_SERVICE = "wallpaper";
field public static final java.lang.String WIFI_AWARE_SERVICE = "wifiaware";
field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p";
@@ -9504,6 +9516,10 @@
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+ field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+ field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -10815,6 +10831,7 @@
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+ field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch";
@@ -21999,6 +22016,7 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
+ method public static int getVolumeControlStream(android.media.AudioAttributes);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -25275,6 +25293,8 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
+ method public void addQueueItem(android.media.MediaDescription);
+ method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -25293,6 +25313,8 @@
method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
+ method public void removeQueueItem(android.media.MediaDescription);
+ method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -25370,11 +25392,14 @@
method public void setSessionActivity(android.app.PendingIntent);
method public void setShuffleModeEnabled(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+ field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
+ method public void onAddQueueItem(android.media.MediaDescription);
+ method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -25388,6 +25413,8 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+ method public void onRemoveQueueItem(android.media.MediaDescription);
+ method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
@@ -39128,26 +39155,19 @@
ctor public AutoFillService();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
- method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
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 void onFillResponseAuthenticationRequest(android.os.Bundle, int);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, 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 int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
- field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
- field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
- field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
- public final class FillCallback {
- method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+ public final class FillCallback implements android.os.Parcelable {
+ method public int describeContents();
method public void onFailure(java.lang.CharSequence);
- method public void onFillResponseAuthentication(int);
method public void onSuccess(android.view.autofill.FillResponse);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
}
public final class SaveCallback {
@@ -45353,6 +45373,15 @@
method public android.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
}
+ public abstract class TransitionListenerAdapter implements android.transition.Transition.TransitionListener {
+ ctor public TransitionListenerAdapter();
+ method public void onTransitionCancel(android.transition.Transition);
+ method public void onTransitionEnd(android.transition.Transition);
+ method public void onTransitionPause(android.transition.Transition);
+ method public void onTransitionResume(android.transition.Transition);
+ method public void onTransitionStart(android.transition.Transition);
+ }
+
public class TransitionManager {
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
@@ -47752,8 +47781,8 @@
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final <T extends android.view.View> T findViewById(int);
- method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
+ method public final android.view.View findViewById(int);
+ method public final android.view.View findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -50160,10 +50189,9 @@
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(java.lang.CharSequence);
+ ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
method public android.view.autofill.Dataset build();
- method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
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);
}
@@ -50175,12 +50203,11 @@
}
public static final class FillResponse.Builder {
- ctor public FillResponse.Builder();
+ ctor public FillResponse.Builder(java.lang.String);
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 requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
}
@@ -50606,7 +50633,7 @@
public final class TextClassificationManager {
method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
- method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+ method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
}
public final class TextClassificationResult {
@@ -54298,6 +54325,7 @@
method public void setIs24HourView(java.lang.Boolean);
method public void setMinute(int);
method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
+ method public boolean validateInput();
}
public static abstract interface TimePicker.OnTimeChangedListener {
@@ -54696,6 +54724,8 @@
field public static final int OP_INVOKE_INTERFACE = 114; // 0x72
field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff
field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78
+ field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa
+ field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb
field public static final int OP_INVOKE_STATIC = 113; // 0x71
field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff
field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77
@@ -57990,12 +58020,15 @@
}
public abstract class MethodHandle {
+ method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asFixedArity();
+ method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+ method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable;
method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
method public boolean isVarargsCollector();
method public java.lang.invoke.MethodType type();
@@ -58029,17 +58062,23 @@
method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+ method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...);
method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandles.Lookup lookup();
method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+ method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int);
method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
}
@@ -58056,6 +58095,7 @@
method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
+ method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
diff --git a/api/test-current.txt b/api/test-current.txt
index bc7a001..b21a4dc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5056,14 +5056,19 @@
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public int getBadgeIcon();
method public java.lang.String getChannel();
method public java.lang.String getGroup();
method public android.graphics.drawable.Icon getLargeIcon();
+ method public java.lang.String getShortcutId();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
+ field public static final int BADGE_ICON_LARGE = 2; // 0x2
+ field public static final int BADGE_ICON_NONE = 0; // 0x0
+ field public static final int BADGE_ICON_SMALL = 1; // 0x1
field public static final java.lang.String CATEGORY_ALARM = "alarm";
field public static final java.lang.String CATEGORY_CALL = "call";
field public static final java.lang.String CATEGORY_EMAIL = "email";
@@ -5155,7 +5160,7 @@
field public deprecated int ledARGB;
field public deprecated int ledOffMS;
field public deprecated int ledOnMS;
- field public deprecated int number;
+ field public int number;
field public deprecated int priority;
field public android.app.Notification publicVersion;
field public deprecated android.net.Uri sound;
@@ -5243,6 +5248,7 @@
method public android.app.Notification.Builder addExtras(android.os.Bundle);
method public android.app.Notification.Builder addPerson(java.lang.String);
method public android.app.Notification build();
+ method public android.app.Notification.Builder chooseBadgeIcon(int);
method public android.widget.RemoteViews createBigContentView();
method public android.widget.RemoteViews createContentView();
method public android.widget.RemoteViews createHeadsUpContentView();
@@ -5275,13 +5281,14 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public deprecated android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public deprecated android.app.Notification.Builder setNumber(int);
+ method public android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public deprecated android.app.Notification.Builder setPriority(int);
method public android.app.Notification.Builder setProgress(int, int, boolean);
method public android.app.Notification.Builder setPublicVersion(android.app.Notification);
method public android.app.Notification.Builder setRemoteInputHistory(java.lang.CharSequence[]);
+ method public android.app.Notification.Builder setShortcutId(java.lang.String);
method public android.app.Notification.Builder setShowWhen(boolean);
method public android.app.Notification.Builder setSmallIcon(int);
method public android.app.Notification.Builder setSmallIcon(int, int);
@@ -9118,6 +9125,10 @@
field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+ field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+ field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+ field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -10359,6 +10370,7 @@
field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+ field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
field public static final java.lang.String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch";
@@ -20535,6 +20547,7 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
+ method public static int getVolumeControlStream(android.media.AudioAttributes);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -23678,6 +23691,8 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
+ method public void addQueueItem(android.media.MediaDescription);
+ method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -23696,6 +23711,8 @@
method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
+ method public void removeQueueItem(android.media.MediaDescription);
+ method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -23773,11 +23790,14 @@
method public void setSessionActivity(android.app.PendingIntent);
method public void setShuffleModeEnabled(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+ field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
+ method public void onAddQueueItem(android.media.MediaDescription);
+ method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -23791,6 +23811,8 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+ method public void onRemoveQueueItem(android.media.MediaDescription);
+ method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
@@ -36231,26 +36253,19 @@
ctor public AutoFillService();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
- method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
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 void onFillResponseAuthenticationRequest(android.os.Bundle, int);
method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, 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 int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
- field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
- field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
- field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
- public final class FillCallback {
- method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+ public final class FillCallback implements android.os.Parcelable {
+ method public int describeContents();
method public void onFailure(java.lang.CharSequence);
- method public void onFillResponseAuthentication(int);
method public void onSuccess(android.view.autofill.FillResponse);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
}
public final class SaveCallback {
@@ -42091,6 +42106,15 @@
method public android.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
}
+ public abstract class TransitionListenerAdapter implements android.transition.Transition.TransitionListener {
+ ctor public TransitionListenerAdapter();
+ method public void onTransitionCancel(android.transition.Transition);
+ method public void onTransitionEnd(android.transition.Transition);
+ method public void onTransitionPause(android.transition.Transition);
+ method public void onTransitionResume(android.transition.Transition);
+ method public void onTransitionStart(android.transition.Transition);
+ }
+
public class TransitionManager {
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
@@ -44656,8 +44680,8 @@
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final <T extends android.view.View> T findViewById(int);
- method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
+ method public final android.view.View findViewById(int);
+ method public final android.view.View findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -47068,10 +47092,9 @@
}
public static final class Dataset.Builder {
- ctor public Dataset.Builder(java.lang.CharSequence);
+ ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
method public android.view.autofill.Dataset build();
- method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
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);
}
@@ -47083,12 +47106,11 @@
}
public static final class FillResponse.Builder {
- ctor public FillResponse.Builder();
+ ctor public FillResponse.Builder(java.lang.String);
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 requiresCustomAuthentication(android.os.Bundle, int);
- method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+ method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
}
@@ -47514,7 +47536,7 @@
public final class TextClassificationManager {
method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
- method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+ method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
}
public final class TextClassificationResult {
@@ -50857,6 +50879,7 @@
method public void setIs24HourView(java.lang.Boolean);
method public void setMinute(int);
method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
+ method public boolean validateInput();
field public static final int MODE_CLOCK = 2; // 0x2
field public static final int MODE_SPINNER = 1; // 0x1
}
@@ -51258,6 +51281,8 @@
field public static final int OP_INVOKE_INTERFACE = 114; // 0x72
field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff
field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78
+ field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa
+ field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb
field public static final int OP_INVOKE_STATIC = 113; // 0x71
field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff
field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77
@@ -54552,12 +54577,15 @@
}
public abstract class MethodHandle {
+ method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asFixedArity();
+ method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int);
method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+ method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable;
method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
method public boolean isVarargsCollector();
method public java.lang.invoke.MethodType type();
@@ -54591,17 +54619,23 @@
method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+ method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...);
method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandles.Lookup lookup();
method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+ method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle);
+ method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int);
method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
}
@@ -54618,6 +54652,7 @@
method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
+ method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 04510da..0b9479d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -33,6 +33,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.util.Pair;
import android.util.Slog;
@@ -1342,7 +1343,7 @@
+ mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
}
- private static class HideWindowListener extends Transition.TransitionListenerAdapter
+ private static class HideWindowListener extends TransitionListenerAdapter
implements ExitTransitionCoordinator.HideSharedElementsCallback {
private final Window mWindow;
private final ExitTransitionCoordinator mExit;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 1eaf029..a512350 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -24,6 +24,7 @@
import android.os.Parcelable;
import android.os.ResultReceiver;
import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionSet;
import android.transition.Visibility;
import android.util.ArrayMap;
@@ -916,7 +917,7 @@
protected void onTransitionsComplete() {}
- protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
+ protected class ContinueTransitionListener extends TransitionListenerAdapter {
@Override
public void onTransitionStart(Transition transition) {
mIsStartingTransition = false;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 0454acb..445b687 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -26,6 +26,7 @@
import android.os.ResultReceiver;
import android.text.TextUtils;
import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.util.ArrayMap;
import android.view.View;
@@ -491,7 +492,7 @@
sharedElementTransitionStarted();
sharedElementTransitionComplete();
} else {
- sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+ sharedElementTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
sharedElementTransitionStarted();
@@ -592,7 +593,7 @@
});
mBackgroundAnimator.start();
} else if (transition != null) {
- transition.addListener(new Transition.TransitionListenerAdapter() {
+ transition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f04eef2..29e10d8 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -31,6 +31,7 @@
import android.os.Message;
import android.os.ResultReceiver;
import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewGroup;
@@ -161,7 +162,7 @@
private void startSharedElementExit(final ViewGroup decorView) {
Transition transition = getSharedElementExitTransition();
- transition.addListener(new Transition.TransitionListenerAdapter() {
+ transition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 4f68ec7..b7c0737 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2077,8 +2077,6 @@
} else {
record.trackAddedFragmentsInPop(mTmpAddedFragments);
}
- final int bumpAmount = isPop ? -1 : 1;
- record.bumpBackStackNesting(bumpAmount);
addToBackStack = addToBackStack || record.mAddToBackStack;
}
mTmpAddedFragments.clear();
@@ -2281,8 +2279,10 @@
final BackStackRecord record = records.get(i);
final boolean isPop = isRecordPop.get(i);
if (isPop) {
+ record.bumpBackStackNesting(-1);
record.executePopOps();
} else {
+ record.bumpBackStackNesting(1);
record.executeOps();
}
}
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index 2570d92..f62ab8d 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -18,6 +18,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.ArrayMap;
@@ -332,7 +333,7 @@
OneShotPreDrawListener.add(exitingFragment.mContainer, () -> {
setViewVisibility(exitingViews, View.INVISIBLE);
});
- exitTransition.addListener(new Transition.TransitionListenerAdapter() {
+ exitTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
@@ -995,7 +996,7 @@
final Transition enterTransition, final ArrayList<View> enteringViews,
final Transition exitTransition, final ArrayList<View> exitingViews,
final TransitionSet sharedElementTransition, final ArrayList<View> sharedElementsIn) {
- overalTransition.addListener(new Transition.TransitionListenerAdapter() {
+ overalTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
if (enterTransition != null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c1cdd6..44e328e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -28,6 +28,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ShortcutInfo;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -223,13 +224,11 @@
* superimposed over the icon in the status bar. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
* {@link Notification.Builder} has displayed the number in the expanded notification view.
+ * Starting with {@link android.os.Build.VERSION_CODES#O}, the number may be displayed as a
+ * badge icon in Launchers that support badging.
*
- * If the number is 0 or negative, it is never shown.
- *
- * @deprecated this number is not shown anymore
*/
- @Deprecated
- public int number;
+ public int number = 1;
/**
* The intent to execute when the expanded status entry is clicked. If
@@ -634,9 +633,10 @@
/**
* Special value of {@link #color} used as a place holder for an invalid color.
+ * @hide
*/
@ColorInt
- private static final int COLOR_INVALID = 1;
+ public static final int COLOR_INVALID = 1;
/**
* Sphere of visibility of this notification, which affects how and when the SystemUI reveals
@@ -1074,6 +1074,26 @@
private String mChannelId;
private long mTimeout;
+ private String mShortcutId;
+
+ /**
+ * If this notification is being shown as a badge, always show as a number.
+ */
+ public static final int BADGE_ICON_NONE = 0;
+
+ /**
+ * If this notification is being shown as a badge, use the {@link #getSmallIcon()} to
+ * represent this notification.
+ */
+ public static final int BADGE_ICON_SMALL = 1;
+
+ /**
+ * If this notification is being shown as a badge, use the {@link #getLargeIcon()} to
+ * represent this notification.
+ */
+ public static final int BADGE_ICON_LARGE = 2;
+ private int mBadgeIcon = BADGE_ICON_LARGE;
+
/**
* Structure to encapsulate a named action that can be shown as part of this notification.
* It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
@@ -1818,6 +1838,12 @@
mChannelId = parcel.readString();
}
mTimeout = parcel.readLong();
+
+ if (parcel.readInt() != 0) {
+ mShortcutId = parcel.readString();
+ }
+
+ mBadgeIcon = parcel.readInt();
}
@Override
@@ -2042,7 +2068,7 @@
}
try {
// IMPORTANT: Add marshaling code in writeToParcelImpl as we
- // want to intercept all pending events written to the pacel.
+ // want to intercept all pending events written to the parcel.
writeToParcelImpl(parcel, flags);
// Must be written last!
parcel.writeArraySet(allPendingIntents);
@@ -2185,6 +2211,15 @@
parcel.writeInt(0);
}
parcel.writeLong(mTimeout);
+
+ if (mShortcutId != null) {
+ parcel.writeInt(1);
+ parcel.writeString(mShortcutId);
+ } else {
+ parcel.writeInt(0);
+ }
+
+ parcel.writeInt(mBadgeIcon);
}
/**
@@ -2382,13 +2417,30 @@
}
/**
- * Returns the time at which this notification should be canceled, if it's not canceled already.
+ * Returns the time at which this notification should be canceled by the system, if it's not
+ * canceled already.
*/
public long getTimeout() {
return mTimeout;
}
/**
+ * Returns what icon should be shown for this notification if it is being displayed in a
+ * Launcher that supports badging. Will be one of {@link #BADGE_ICON_NONE},
+ * {@link #BADGE_ICON_SMALL}, or {@link #BADGE_ICON_LARGE}.
+ */
+ public int getBadgeIcon() {
+ return mBadgeIcon;
+ }
+
+ /**
+ * Returns the {@link ShortcutInfo#getId() id} that this notification supersedes, if any.
+ */
+ public String getShortcutId() {
+ return mShortcutId;
+ }
+
+ /**
* The small icon representing this notification in the status bar and content view.
*
* @return the small icon representing this notification.
@@ -2483,7 +2535,6 @@
private NotificationColorUtil mColorUtil;
private boolean mIsLegacy;
private boolean mIsLegacyInitialized;
- private boolean mColorUtilInited = false;
/**
* Caches a contrast-enhanced version of {@link #mCachedContrastColorIsFor}.
@@ -2601,16 +2652,42 @@
}
private NotificationColorUtil getColorUtil() {
- if (!mColorUtilInited) {
- mColorUtilInited = true;
- if (isLegacy() || isColorized()) {
- mColorUtil = NotificationColorUtil.getInstance(mContext);
- }
+ if (mColorUtil == null) {
+ mColorUtil = NotificationColorUtil.getInstance(mContext);
}
return mColorUtil;
}
/**
+ * If this notification is duplicative of a Launcher shortcut, sets the
+ * {@link ShortcutInfo#getId() id} of the shortcut, in case the Launcher wants to hide
+ * the shortcut.
+ *
+ * This field will be ignored by Launchers that don't support badging or
+ * {@link android.content.pm.ShortcutManager shortcuts}.
+ *
+ * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
+ * supersedes
+ */
+ public Builder setShortcutId(String shortcutId) {
+ mN.mShortcutId = shortcutId;
+ return this;
+ }
+
+ /**
+ * Sets which icon to display as a badge for this notification.
+ *
+ * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
+ * {@link #BADGE_ICON_LARGE}.
+ *
+ * Note: This value might be ignored, for launchers that don't support badge icons.
+ */
+ public Builder chooseBadgeIcon(int icon) {
+ mN.mBadgeIcon = icon;
+ return this;
+ }
+
+ /**
* Specifies the channel the notification should be delivered on.
*/
public Builder setChannel(String channelId) {
@@ -2805,13 +2882,9 @@
}
/**
- * Set the large number at the right-hand side of the notification. This is
- * equivalent to setContentInfo, although it might show the number in a different
- * font size for readability.
- *
- * @deprecated this number is not shown anywhere anymore
+ * Sets the number of items this notification represents. May be displayed as a badge count
+ * for Launchers that support badging.
*/
- @Deprecated
public Builder setNumber(int number) {
mN.number = number;
return this;
@@ -3096,12 +3169,16 @@
* Set whether this notification should be colorized. When set, the color set with
* {@link #setColor(int)} will be used as the background color of this notification.
* <p>
- * The coloring will only be applied if the notification is ongoing.
* This should only be used for high priority ongoing tasks like navigation, an ongoing
* call, or other similarly high-priority events for the user.
+ * <p>
+ * For most styles, the coloring will only be applied if the notification is ongoing.
+ * However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
+ * that have a media session attached there is no requirement for it to be ongoing.
*
* @see Builder#setOngoing(boolean)
* @see Builder#setColor(int)
+ * @see MediaStyle#setMediaSession(MediaSession.Token)
*/
public Builder setColorized(boolean colorize) {
mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
@@ -3682,13 +3759,21 @@
}
private void bindExpandButton(RemoteViews contentView) {
- int color = isColorized() ? getPrimaryTextColor() : resolveContrastColor();
+ int color = getPrimaryHighlightColor();
contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
PorterDuff.Mode.SRC_ATOP, -1);
contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
color);
}
+ /**
+ * @return the color that is used as the first primary highlight color. This is applied
+ * in several places like the action buttons or the app name in the header.
+ */
+ private int getPrimaryHighlightColor() {
+ return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
+ }
+
private void bindHeaderChronometerAndTime(RemoteViews contentView) {
if (showsTimeOrChronometer()) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
@@ -4265,7 +4350,7 @@
}
private CharSequence processLegacyText(CharSequence charSequence) {
- if (isLegacy() || isColorized()) {
+ if (isLegacy() || textColorsNeedInversion()) {
return getColorUtil().invertCharSequenceColors(charSequence);
} else {
return charSequence;
@@ -4278,7 +4363,7 @@
private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
boolean ambient) {
boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
- int color = ambient ? resolveAmbientColor() : resolveContrastColor();
+ int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
if (colorable) {
contentView.setDrawableParameters(R.id.icon, false, -1, color,
PorterDuff.Mode.SRC_ATOP, -1);
@@ -4539,6 +4624,15 @@
private boolean isColorized() {
return mN.isColorized();
}
+
+ private boolean textColorsNeedInversion() {
+ if (mStyle == null || !MediaStyle.class.equals(mStyle.getClass())) {
+ return false;
+ }
+ int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ return targetSdkVersion > Build.VERSION_CODES.M
+ && targetSdkVersion < Build.VERSION_CODES.O;
+ }
}
/**
@@ -4551,6 +4645,14 @@
}
/**
+ * @return whether this notification has a media session attached
+ * @hide
+ */
+ public boolean hasMediaSession() {
+ return extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
+ }
+
+ /**
* @return the style class of this notification
* @hide
*/
@@ -4564,12 +4666,23 @@
}
/**
- * @return true if this notification is colorized. This also factors in wheather the
+ * @return true if this notification is colorized. This also factors in whether the
* notification is ongoing.
*
* @hide
*/
public boolean isColorized() {
+ Class<? extends Style> style = getNotificationStyle();
+ if (MediaStyle.class.equals(style)) {
+ Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
+ if ((colorized == null || colorized) && hasMediaSession()) {
+ return true;
+ }
+ } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
+ if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
+ return true;
+ }
+ }
return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
}
@@ -5815,21 +5928,27 @@
* shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
* {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
* treated as album artwork.
- *
+ * <p>
* Unlike the other styles provided here, MediaStyle can also modify the standard-size
* {@link Notification#contentView}; by providing action indices to
* {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed
* in the standard view alongside the usual content.
- *
+ * <p>
* Notifications created with MediaStyle will have their category set to
* {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different
* category using {@link Notification.Builder#setCategory(String) setCategory()}.
- *
+ * <p>
* Finally, if you attach a {@link android.media.session.MediaSession.Token} using
* {@link android.app.Notification.MediaStyle#setMediaSession(MediaSession.Token)},
* the System UI can identify this as a notification representing an active media session
* and respond accordingly (by showing album artwork in the lockscreen, for example).
*
+ * <p>
+ * Starting at {@link android.os.Build.VERSION_CODES#O Android O} any notification that has a
+ * media session attached with {@link #setMediaSession(MediaSession.Token)} will be colorized.
+ * You can opt-out of this behavior by using {@link Notification.Builder#setColorized(boolean)}.
+ * <p>
+ *
* To use this style with your Notification, feed it to
* {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
* <pre class="prettyprint">
@@ -5844,6 +5963,7 @@
* </pre>
*
* @see Notification#bigContentView
+ * @see Notification.Builder#setColorized(boolean)
*/
public static class MediaStyle extends Style {
static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
@@ -5981,7 +6101,7 @@
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
final RemoteViews button = generateMediaActionButton(action,
- mBuilder.resolveContrastColor());
+ getPrimaryHighlightColor());
view.addView(com.android.internal.R.id.media_actions, button);
}
}
@@ -5995,6 +6115,10 @@
return view;
}
+ private int getPrimaryHighlightColor() {
+ return mBuilder.getPrimaryHighlightColor();
+ }
+
private RemoteViews makeMediaBigContentView() {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
// Dont add an expanded view if there is no more content to be revealed
@@ -6012,7 +6136,7 @@
big.removeAllViews(com.android.internal.R.id.media_actions);
for (int i = 0; i < actionCount; i++) {
final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
- mBuilder.resolveContrastColor());
+ getPrimaryHighlightColor());
big.addView(com.android.internal.R.id.media_actions, button);
}
}
@@ -6157,7 +6281,10 @@
* {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and
* {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the
* corresponding custom views to display.
- *
+ * <p>
+ * Contrary to {@link MediaStyle} a developer has to opt-in to the colorizing of the
+ * notification by using {@link Notification.Builder#setColorized(boolean)}.
+ * <p>
* To use this style with your Notification, feed it to
* {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
* <pre class="prettyprint">
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5a75a67..fc1d613 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -115,6 +115,7 @@
import android.service.autofill.IAutoFillManagerService;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.service.vr.IVrManager;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
@@ -814,6 +815,14 @@
IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b);
return new AutoFillManager(ctx, service);
}});
+
+ registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
+ @Override
+ public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.VR_SERVICE);
+ return new VrManager(IVrManager.Stub.asInterface(b));
+ }
+ });
}
/**
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 3f467a0..b219f2a 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -145,14 +145,25 @@
}
@Override
+ public void show() {
+ super.show();
+ getButton(BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mTimePicker.validateInput()) {
+ if (mTimeSetListener != null) {
+ mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
+ mTimePicker.getCurrentMinute());
+ }
+ dismiss();
+ }
+ }
+ });
+ }
+
+ @Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
- case BUTTON_POSITIVE:
- if (mTimeSetListener != null) {
- mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
- mTimePicker.getCurrentMinute());
- }
- break;
case BUTTON_NEGATIVE:
cancel();
break;
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
new file mode 100644
index 0000000..a0b0eea
--- /dev/null
+++ b/core/java/android/app/VrManager.java
@@ -0,0 +1,44 @@
+package android.app;
+
+
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.os.RemoteException;
+import android.service.vr.IVrManager;
+
+/**
+ * Used to control aspects of a devices Virtual Reality (VR) capabilities.
+ * <p>
+ * You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService}.
+ * @hide
+ */
+@SystemApi
+public class VrManager {
+ private final IVrManager mService;
+
+ /**
+ * {@hide}
+ */
+ public VrManager(IVrManager service) {
+ mService = service;
+ }
+
+ /**
+ * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
+ * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used
+ * by VR viewers to indicate that a device is placed in a VR viewer.
+ *
+ * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
+ *
+ * @see Activity#setVrModeEnabled(boolean, ComponentName)
+ * @param enabled true if the device should be placed in persistent VR mode.
+ */
+ public void setPersistentVrModeEnabled(boolean enabled) {
+ try {
+ mService.setPersistentVrModeEnabled(enabled);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 2858991..91b87d7 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -144,8 +144,7 @@
}
/**
- * Returns the payload contained in this log. Each call to this method will
- * retrieve the next payload item. If no more payload exists, it returns {@code null}.
+ * Returns the payload contained in this log entry or {@code null} if there is no payload.
*/
public Object getData() {
return mEvent.getData();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f610a29..a7d5ac6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -35,6 +35,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.Notification;
+import android.app.VrManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -3764,6 +3765,16 @@
public static final String OVERLAY_SERVICE = "overlay";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link VrManager} for accessing the VR service.
+ *
+ * @see #getSystemService
+ * @hide
+ */
+ @SystemApi
+ public static final String VR_SERVICE = "vrmanager";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1130327..6a8141f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1806,6 +1806,41 @@
@SystemApi
public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
+ /**
+ * Intent extra: An id if an autofill item ({@link
+ * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
+ * <p>
+ * Type: String
+ * </p>
+ */
+ public static final String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
+
+ /**
+ * Intent extra: The assist structure which captures the filled screen.
+ * <p>
+ * Type: {@link android.app.assist.AssistStructure}
+ * </p>
+ */
+ public static final String EXTRA_AUTO_FILL_ASSIST_STRUCTURE =
+ "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+
+ /**
+ * Intent extra: The metadata associated with the authenticated entity ({@link
+ * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
+ * <p>
+ * Type: {@link android.os.Bundle}
+ * </p>
+ */
+ public static final String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+
+ /**
+ * Intent extra: A callback to report an authentication result.
+ * <p>
+ * Type: {@link android.view.autofill.FillResponse}
+ * </p>
+ */
+ public static final String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index dd53eac..74d2f11 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -351,7 +351,7 @@
* @param requiresCharging true if sync requires the phone to be plugged in. Default false.
*/
public Builder setRequiresCharging(boolean requiresCharging) {
- mRequiresCharging = true;
+ mRequiresCharging = requiresCharging;
return this;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ab641c9..b20b5e2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1741,6 +1741,22 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API
+ * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate
+ * what level of optional compute features are supported beyond the Vulkan 1.0 requirements.
+ * <p>
+ * Compute level 0 indicates support for:
+ * <ul>
+ * <li>Ability to use pointers to buffer data from shaders</li>
+ * <li>Ability to load/store 16-bit values from buffers</li>
+ * <li>Ability to control shader floating point rounding mode</li>
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature(String, int)}: The version of this feature indicates the highest
* {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices that support
* the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The feature version
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 3eb5844..98a5749 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -230,9 +230,6 @@
public int dozeScreenBrightness;
public int dozeScreenState;
- // If true, use twilight to affect the brightness.
- public boolean useTwilight;
-
public DisplayPowerRequest() {
policy = POLICY_BRIGHT;
useProximitySensor = false;
@@ -268,7 +265,6 @@
boostScreenBrightness = other.boostScreenBrightness;
dozeScreenBrightness = other.dozeScreenBrightness;
dozeScreenState = other.dozeScreenState;
- useTwilight = other.useTwilight;
}
@Override
@@ -289,8 +285,7 @@
&& lowPowerMode == other.lowPowerMode
&& boostScreenBrightness == other.boostScreenBrightness
&& dozeScreenBrightness == other.dozeScreenBrightness
- && dozeScreenState == other.dozeScreenState
- && useTwilight == other.useTwilight;
+ && dozeScreenState == other.dozeScreenState;
}
@Override
@@ -310,8 +305,7 @@
+ ", lowPowerMode=" + lowPowerMode
+ ", boostScreenBrightness=" + boostScreenBrightness
+ ", dozeScreenBrightness=" + dozeScreenBrightness
- + ", dozeScreenState=" + Display.stateToString(dozeScreenState)
- + ", useTwilight=" + useTwilight;
+ + ", dozeScreenState=" + Display.stateToString(dozeScreenState);
}
public static String policyToString(int policy) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e99d303..f94e89a 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.IntegerRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -1339,7 +1340,7 @@
}
/**
- * Flatten a heterogeneous array containing a particular object type into
+ * Flatten a homogeneous array containing a particular object type into
* the parcel, at
* the current dataPosition() and growing dataCapacity() if needed. The
* type of the objects in the array must be one that implements Parcelable.
@@ -1361,7 +1362,7 @@
if (val != null) {
int N = val.length;
writeInt(N);
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
T item = val[i];
if (item != null) {
writeInt(1);
@@ -1376,6 +1377,146 @@
}
/**
+ * Write a uniform (all items are null or the same class) array list of
+ * parcelables.
+ *
+ * @param list The list to write.
+ *
+ * @hide
+ */
+ public final <T extends Parcelable> void writeTypedArrayList(@Nullable ArrayList<T> list,
+ int parcelableFlags) {
+ if (list != null) {
+ int N = list.size();
+ writeInt(N);
+ boolean wroteCreator = false;
+ for (int i = 0; i < N; i++) {
+ T item = list.get(i);
+ if (item != null) {
+ writeInt(1);
+ if (!wroteCreator) {
+ writeParcelableCreator(item);
+ wroteCreator = true;
+ }
+ item.writeToParcel(this, parcelableFlags);
+ } else {
+ writeInt(0);
+ }
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
+ * Reads a uniform (all items are null or the same class) array list of
+ * parcelables.
+ *
+ * @return The list or null.
+ *
+ * @hide
+ */
+ public final @Nullable <T> ArrayList<T> readTypedArrayList(@Nullable ClassLoader loader) {
+ int N = readInt();
+ if (N <= 0) {
+ return null;
+ }
+ Parcelable.Creator<?> creator = null;
+ ArrayList<T> result = new ArrayList<T>(N);
+ for (int i = 0; i < N; i++) {
+ if (readInt() != 0) {
+ if (creator == null) {
+ creator = readParcelableCreator(loader);
+ if (creator == null) {
+ return null;
+ }
+ }
+ final T parcelable;
+ if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
+ } else {
+ parcelable = (T) creator.createFromParcel(this);
+ }
+ result.add(parcelable);
+ } else {
+ result.add(null);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Write a uniform (all items are null or the same class) array set of
+ * parcelables.
+ *
+ * @param set The set to write.
+ *
+ * @hide
+ */
+ public final <T extends Parcelable> void writeTypedArraySet(@Nullable ArraySet<T> set,
+ int parcelableFlags) {
+ if (set != null) {
+ int N = set.size();
+ writeInt(N);
+ boolean wroteCreator = false;
+ for (int i = 0; i < N; i++) {
+ T item = set.valueAt(i);
+ if (item != null) {
+ writeInt(1);
+ if (!wroteCreator) {
+ writeParcelableCreator(item);
+ wroteCreator = true;
+ }
+ item.writeToParcel(this, parcelableFlags);
+ } else {
+ writeInt(0);
+ }
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
+ * Reads a uniform (all items are null or the same class) array set of
+ * parcelables.
+ *
+ * @return The set or null.
+ *
+ * @hide
+ */
+ public final @Nullable <T> ArraySet<T> readTypedArraySet(@Nullable ClassLoader loader) {
+ int N = readInt();
+ if (N <= 0) {
+ return null;
+ }
+ Parcelable.Creator<?> creator = null;
+ ArraySet<T> result = new ArraySet<T>(N);
+ for (int i = 0; i < N; i++) {
+ T parcelable = null;
+ if (readInt() != 0) {
+ if (creator == null) {
+ creator = readParcelableCreator(loader);
+ if (creator == null) {
+ return null;
+ }
+ }
+ if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
+ } else {
+ parcelable = (T) creator.createFromParcel(this);
+ }
+ }
+ result.append(parcelable);
+ }
+ return result;
+ }
+
+ /**
* Flatten the Parcelable object into the parcel.
*
* @param val The Parcelable object to be written.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dff0a28..31b3bc9 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -508,15 +508,6 @@
}
/**
- * Returns true if the twilight service should be used to adjust screen brightness
- * policy. This setting is experimental and disabled by default.
- * @hide
- */
- public static boolean useTwilightAdjustmentFeature() {
- return SystemProperties.getBoolean("persist.power.usetwilightadj", false);
- }
-
- /**
* Creates a new wake lock with the specified level and flags.
* <p>
* The {@code levelAndFlags} parameter specifies a wake lock level and optional flags
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d83f2cb..2936dfb 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6721,12 +6721,6 @@
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
/**
- * Whether brightness should automatically adjust based on twilight state.
- * @hide
- */
- public static final String BRIGHTNESS_USE_TWILIGHT = "brightness_use_twilight";
-
- /**
* Names of the service components that the current user has explicitly allowed to
* be a VR mode listener, separated by ':'.
*
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index bfaf23c..aa4d26c 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,7 +15,10 @@
*/
package android.service.autofill;
-import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import com.android.internal.os.HandlerCaller;
import android.annotation.SdkConstant;
import android.app.Activity;
import android.app.Service;
@@ -24,21 +27,13 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
import android.util.Log;
-import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
-import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
//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.
@@ -48,12 +43,7 @@
* <p>Apps providing auto-fill capabilities must extend this service.
*/
public abstract class AutoFillService extends Service {
-
private static final String TAG = "AutoFillService";
- static final boolean DEBUG = true; // TODO(b/33197203): set to false once stable
-
- // TODO(b/33197203): check for device's memory size instead of DEBUG?
- static final boolean DEBUG_PENDING_CALLBACKS = DEBUG;
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -79,85 +69,33 @@
// Internal bundle keys.
/** @hide */ public static final String KEY_CALLBACK = "callback";
- /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids";
- /** @hide */ public static final String EXTRA_CRYPTO_OBJECT_ID = "crypto_object_id";
-
- // 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, 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, 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";
-
- /**
- * Used to indicate the user selected an action that requires authentication.
- */
- public static final int FLAG_AUTHENTICATION_REQUESTED = 1 << 0;
-
- /**
- * Used to indicate the user authentication succeeded.
- */
- public static final int FLAG_AUTHENTICATION_SUCCESS = 1 << 1;
-
- /**
- * Used to indicate the user authentication failed.
- */
- public static final int FLAG_AUTHENTICATION_ERROR = 1 << 2;
-
- /**
- * Used when the service requested Fingerprint authentication but such option is not available.
- */
- public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 1 << 3;
// Handler messages.
private static final int MSG_CONNECT = 1;
private static final int MSG_DISCONNECT = 2;
- private static final int MSG_AUTO_FILL_ACTIVITY = 3;
- private static final int MSG_AUTHENTICATE_FILL_RESPONSE = 4;
- private static final int MSG_AUTHENTICATE_DATASET = 5;
- private static final int MSG_SAVE = 6;
+ private static final int MSG_ON_FILL_REQUEST = 3;
+ private static final int MSG_ON_SAVE_REQUEST = 4;
private final IAutoFillService mInterface = new IAutoFillService.Stub() {
-
@Override
- public void autoFill(AssistStructure structure, IAutoFillServerCallback callback) {
- mHandlerCaller
- .obtainMessageOO(MSG_AUTO_FILL_ACTIVITY, structure, callback)
+ public void onFillRequest(AssistStructure structure, Bundle extras,
+ IFillCallback callback) {
+ ICancellationSignal transport = CancellationSignal.createTransport();
+ try {
+ callback.onCancellable(transport);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ mHandlerCaller.obtainMessageOOOO(MSG_ON_FILL_REQUEST, structure,
+ CancellationSignal.fromTransport(transport), extras, callback)
.sendToTarget();
}
@Override
- public void save(AssistStructure structure, IAutoFillServerCallback callback,
- Bundle extras) throws RemoteException {
- mHandlerCaller
- .obtainMessageOOO(MSG_SAVE, structure, callback, extras)
- .sendToTarget();
- }
-
- @Override
- public void authenticateFillResponse(Bundle extras, int flags) {
- final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_FILL_RESPONSE);
- msg.arg1 = flags;
- msg.obj = extras;
- mHandlerCaller.sendMessage(msg);
- }
-
- @Override
- public void authenticateDataset(Bundle extras, int flags) {
- final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_DATASET);
- msg.arg1 = flags;
- msg.obj = extras;
- mHandlerCaller.sendMessage(msg);
+ public void onSaveRequest(AssistStructure structure, Bundle extras,
+ ISaveCallback callback) {
+ mHandlerCaller.obtainMessageOOO(MSG_ON_SAVE_REQUEST, structure,
+ extras, callback).sendToTarget();
}
@Override
@@ -171,63 +109,41 @@
}
};
- private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
-
- @Override
- public void executeMessage(Message msg) {
- switch (msg.what) {
- case MSG_CONNECT: {
- onConnected();
- break;
- } case MSG_AUTO_FILL_ACTIVITY: {
- final SomeArgs args = (SomeArgs) msg.obj;
- try {
- final AssistStructure structure = (AssistStructure) args.arg1;
- final IAutoFillServerCallback callback =
- (IAutoFillServerCallback) args.arg2;
- handleAutoFill(structure, callback);
- } finally {
- args.recycle();
- }
- break;
- } case MSG_SAVE: {
- final SomeArgs args = (SomeArgs) msg.obj;
- try {
- final AssistStructure structure = (AssistStructure) args.arg1;
- final IAutoFillServerCallback callback =
- (IAutoFillServerCallback) args.arg2;
- final Bundle extras = (Bundle) args.arg3;
- handleSave(structure, callback, extras);
- } finally {
- args.recycle();
- }
- break;
- } case MSG_AUTHENTICATE_FILL_RESPONSE: {
- final int flags = msg.arg1;
- final Bundle extras = (Bundle) msg.obj;
- onFillResponseAuthenticationRequest(extras, flags);
- break;
- } case MSG_AUTHENTICATE_DATASET: {
- final int flags = msg.arg1;
- final Bundle extras = (Bundle) msg.obj;
- onDatasetAuthenticationRequest(extras, flags);
- break;
- } case MSG_DISCONNECT: {
- onDisconnected();
- break;
- } default: {
- Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
- }
+ private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
+ switch (msg.what) {
+ case MSG_CONNECT: {
+ onConnected();
+ break;
+ } case MSG_ON_FILL_REQUEST: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final AssistStructure structure = (AssistStructure) args.arg1;
+ final CancellationSignal cancellation = (CancellationSignal) args.arg2;
+ final Bundle extras = (Bundle) args.arg3;
+ final IFillCallback callback = (IFillCallback) args.arg4;
+ final FillCallback fillCallback = new FillCallback(callback);
+ args.recycle();
+ onFillRequest(structure, extras, cancellation, fillCallback);
+ break;
+ } case MSG_ON_SAVE_REQUEST: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final AssistStructure structure = (AssistStructure) args.arg1;
+ final Bundle extras = (Bundle) args.arg2;
+ final ISaveCallback callback = (ISaveCallback) args.arg3;
+ final SaveCallback saveCallback = new SaveCallback(callback);
+ args.recycle();
+ onSaveRequest(structure, extras, saveCallback);
+ break;
+ } case MSG_DISCONNECT: {
+ onDisconnected();
+ break;
+ } default: {
+ Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
}
}
};
private HandlerCaller mHandlerCaller;
- // User for debugging purposes
- private final List<CallbackHelper.Dumpable> mPendingCallbacks =
- DEBUG_PENDING_CALLBACKS ? new ArrayList<>() : null;
-
/**
* {@inheritDoc}
*
@@ -236,7 +152,6 @@
@Override
public void onCreate() {
super.onCreate();
-
mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
}
@@ -255,7 +170,7 @@
* <p>You should generally do initialization here rather than in {@link #onCreate}.
*/
public void onConnected() {
- if (DEBUG) Log.d(TAG, "onConnected()");
+
}
/**
@@ -267,14 +182,18 @@
* to notify the result of the request.
*
* @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 data bundle containing data passed by the service on previous calls to fill.
+ * This bundle allows your service to keep state between fill and save requests
+ * as well as when filling different sections of the UI as the system will try to
+ * aggressively unbind from the service to conserve resources. See {@link FillResponse}
+ * Javadoc for examples of multiple-sections requests.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
* @param callback object used to notify the result of the request.
*/
- public abstract void onFillRequest(AssistStructure structure, Bundle data,
- CancellationSignal cancellationSignal, FillCallback callback);
+ public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
/**
* Called when user requests service to save the fields of an {@link Activity}.
@@ -284,108 +203,15 @@
* to notify the result of the request.
*
* @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 in the {@link FillResponse} that originated this call.
+ * @param data bundle containing data passed by the service on previous calls to fill.
+ * This bundle allows your service to keep state between fill and save requests
+ * as well as when filling different sections of the UI as the system will try to
+ * aggressively unbind from the service to conserve resources. See {@link FillResponse}
+ * Javadoc for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
*/
- public abstract void onSaveRequest(AssistStructure structure, Bundle data,
- SaveCallback callback);
-
- /**
- * Called as result of the user action for a {@link FillResponse} that required authentication.
- *
- * <p>When the {@link FillResponse} required authentication through
- * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)},
- * this call indicates the user is requesting the service to authenticate him/her (and
- * {@code flags} contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains
- * the {@link Bundle} passed to that method.
- *
- * <p>When the {@link FillResponse} required authentication through
- * {@link android.view.autofill.FillResponse.Builder#requiresFingerprintAuthentication(
- * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
- * {@code flags} this call contains the result of the fingerprint authentication (such as
- * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
- * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
- * {@link Bundle} passed to that method.
- */
- public void onFillResponseAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
- int flags) {
- if (DEBUG) Log.d(TAG, "onFillResponseAuthenticationRequest(): flags=" + flags);
- }
-
- /**
- * Called as result of the user action for a {@link Dataset} that required authentication.
- *
- * <p>When the {@link Dataset} required authentication through
- * {@link android.view.autofill.Dataset.Builder#requiresCustomAuthentication(Bundle, int)}, this
- * call indicates the user is requesting the service to authenticate him/her (and {@code flags}
- * contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains the
- * {@link Bundle} passed to that method.
- *
- * <p>When the {@link Dataset} required authentication through
- * {@link android.view.autofill.Dataset.Builder#requiresFingerprintAuthentication(
- * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
- * {@code flags} this call contains the result of the fingerprint authentication (such as
- * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
- * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
- * {@link Bundle} passed to that method.
- */
- public void onDatasetAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
- int flags) {
- if (DEBUG) Log.d(TAG, "onDatasetAuthenticationRequest(): flags=" + flags);
- }
-
- @Override
- @CallSuper
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mPendingCallbacks != null) {
- pw.print("Number of pending callbacks: "); pw.println(mPendingCallbacks.size());
- final String prefix = " ";
- for (int i = 0; i < mPendingCallbacks.size(); i++) {
- final CallbackHelper.Dumpable cb = mPendingCallbacks.get(i);
- pw.print('#'); pw.print(i + 1); pw.println(':');
- cb.dump(prefix, pw);
- }
- pw.println();
- } else {
- pw.println("Dumping disabled");
- }
- }
-
- private void handleAutoFill(AssistStructure structure, IAutoFillServerCallback callback) {
- final FillCallback fillCallback = new FillCallback(callback);
- if (DEBUG_PENDING_CALLBACKS) {
- addPendingCallback(fillCallback);
- }
- // TODO(b/33197203): hook up the cancelationSignal
- onFillRequest(structure, null, new CancellationSignal(), fillCallback);
- return;
- }
-
- private void handleSave(AssistStructure structure, IAutoFillServerCallback callback,
- Bundle extras) {
- final SaveCallback saveCallback = new SaveCallback(callback);
- if (DEBUG_PENDING_CALLBACKS) {
- addPendingCallback(saveCallback);
- }
- onSaveRequest(structure, extras, saveCallback);
- }
-
- private void addPendingCallback(CallbackHelper.Dumpable callback) {
- if (mPendingCallbacks == null) {
- // Shouldn't happend since call is controlled by DEBUG_PENDING_CALLBACKS guard.
- Log.wtf(TAG, "addPendingCallback(): mPendingCallbacks not set");
- return;
- }
-
- if (DEBUG) Log.d(TAG, "Adding pending callback: " + callback);
-
- callback.setFinalizer(() -> {
- if (DEBUG) Log.d(TAG, "Removing pending callback: " + callback);
- mPendingCallbacks.remove(callback);
- });
- mPendingCallbacks.add(callback);
- }
+ public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
+ @NonNull SaveCallback callback);
/**
* Called when the Android system disconnects from the service.
@@ -393,6 +219,6 @@
* <p> At this point this service may no longer be an active {@link AutoFillService}.
*/
public void onDisconnected() {
- if (DEBUG) Log.d(TAG, "onDisconnected()");
+
}
}
diff --git a/core/java/android/service/autofill/AutoFillServiceInfo.java b/core/java/android/service/autofill/AutoFillServiceInfo.java
index fd957f1..985e32f 100644
--- a/core/java/android/service/autofill/AutoFillServiceInfo.java
+++ b/core/java/android/service/autofill/AutoFillServiceInfo.java
@@ -16,6 +16,7 @@
package android.service.autofill;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.ComponentName;
@@ -25,7 +26,6 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.RemoteException;
-import android.util.AndroidException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -40,13 +40,10 @@
/**
* {@link ServiceInfo} and meta-data about an {@link AutoFillService}.
*
- * <p>Upon construction, if {@link #getParseError()} is {@code null}, then the service is configured
- * correctly. Otherwise, {@link #getParseError()} indicates the parsing error.
- *
* @hide
*/
public final class AutoFillServiceInfo {
- static final String TAG = "AutoFillServiceInfo";
+ private static final String TAG = "AutoFillServiceInfo";
private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
@@ -63,10 +60,9 @@
throw new PackageManager.NameNotFoundException(comp.toString());
}
- @Nullable
- private final String mParseError;
-
+ @NonNull
private final ServiceInfo mServiceInfo;
+
@Nullable
private final String mSettingsActivity;
@@ -77,17 +73,7 @@
public AutoFillServiceInfo(PackageManager pm, ServiceInfo si) {
mServiceInfo = si;
- TypedArray metaDataArray;
- try {
- metaDataArray = getMetaDataArray(pm, si);
- } catch (AndroidException e) {
- mParseError = e.getMessage();
- mSettingsActivity = null;
- Log.w(TAG, mParseError, e);
- return;
- }
-
- mParseError = null;
+ final TypedArray metaDataArray = getMetaDataArray(pm, si);
if (metaDataArray != null) {
mSettingsActivity =
metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
@@ -101,12 +87,11 @@
* Gets the meta-data as a TypedArray, or null if not provided, or throws if invalid.
*/
@Nullable
- private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si)
- throws AndroidException {
+ private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
// Check for permissions.
if (!Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
- throw new AndroidException(
- "Service does not require permission " + Manifest.permission.BIND_AUTO_FILL);
+ Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTO_FILL);
+ return null;
}
// Get the AutoFill metadata, if declared.
@@ -125,11 +110,13 @@
&& type != XmlPullParser.START_TAG) {
}
} catch (XmlPullParserException | IOException e) {
- throw new AndroidException("Error parsing auto fill service meta-data: " + e, e);
+ Log.e(TAG, "Error parsing auto fill service meta-data", e);
+ return null;
}
if (!"autofill-service".equals(parser.getName())) {
- throw new AndroidException("Meta-data does not start with autofill-service tag");
+ Log.e(TAG, "Meta-data does not start with autofill-service tag");
+ return null;
}
attrs = Xml.asAttributeSet(parser);
@@ -138,7 +125,8 @@
try {
res = pm.getResourcesForApplication(si.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
- throw new AndroidException("Error getting application resources: " + e, e);
+ Log.e(TAG, "Error getting application resources", e);
+ return null;
}
return res.obtainAttributes(attrs, R.styleable.AutoFillService);
@@ -147,11 +135,6 @@
}
}
- @Nullable
- public String getParseError() {
- return mParseError;
- }
-
public ServiceInfo getServiceInfo() {
return mServiceInfo;
}
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 7cab7ae..a306809 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -16,56 +16,32 @@
package android.service.autofill;
-import static android.service.autofill.AutoFillService.DEBUG;
-import static android.util.DebugUtils.flagsToString;
-
import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
-import android.service.autofill.CallbackHelper.Dumpable;
-import android.service.autofill.CallbackHelper.Finalizer;
-import android.util.Log;
-import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-
/**
* Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
* auto-filled.
- *
- * <p>This class is thread safe.
*/
-public final class FillCallback implements Dumpable {
-
- private static final String TAG = "FillCallback";
-
- // NOTE: constants below are public so they can be used by flagsToString()
- /** @hide */ public static final int STATE_INITIAL = 1 << 0;
- /** @hide */ public static final int STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE = 1 << 1;
- /** @hide */ public static final int STATE_WAITING_DATASET_AUTH_RESPONSE = 1 << 2;
- /** @hide */ public static final int STATE_FINISHED_OK = 1 << 3;
- /** @hide */ public static final int STATE_FINISHED_FAILURE = 1 << 4;
- /** @hide */ public static final int STATE_FINISHED_ERROR = 1 << 5;
- /** @hide */ public static final int STATE_FINISHED_AUTHENTICATED = 1 << 6;
-
- private final IAutoFillServerCallback mCallback;
-
- @GuardedBy("mCallback")
- private int mState = STATE_INITIAL;
-
- @GuardedBy("mCallback")
- private Finalizer mFinalizer;
+public final class FillCallback implements Parcelable {
+ private final IFillCallback mCallback;
+ private boolean mCalled;
/** @hide */
- FillCallback(IAutoFillServerCallback callback) {
+ public FillCallback(IFillCallback callback) {
mCallback = callback;
}
+ /** @hide */
+ private FillCallback(Parcel parcel) {
+ mCallback = IFillCallback.Stub.asInterface(parcel.readStrongBinder());
+ }
+
/**
* Notifies the Android System that an
* {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
@@ -76,43 +52,12 @@
* {@link FillResponse} for examples.
*/
public void onSuccess(@Nullable FillResponse response) {
- final boolean authRequired = response != null && response.isAuthRequired();
-
- if (DEBUG) Log.d(TAG, "onSuccess(): authReq= " + authRequired + ", resp=" + response);
-
- synchronized (mCallback) {
- if (authRequired) {
- assertOnStateLocked(STATE_INITIAL);
- } else {
- assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
- | STATE_WAITING_DATASET_AUTH_RESPONSE);
- }
-
- try {
- mCallback.showResponse(response);
- if (authRequired) {
- mState = STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE;
- } else {
- // Check if at least one dataset requires authentication.
- boolean waitingAuth = false;
- if (response != null) {
- for (Dataset dataset : response.getDatasets()) {
- if (dataset.isAuthRequired()) {
- waitingAuth = true;
- break;
- }
- }
- }
- if (waitingAuth) {
- mState = STATE_WAITING_DATASET_AUTH_RESPONSE;
- } else {
- setFinalStateLocked(STATE_FINISHED_OK);
- }
- }
- } catch (RemoteException e) {
- setFinalStateLocked(STATE_FINISHED_ERROR);
- e.rethrowAsRuntimeException();
- }
+ assertNotCalled();
+ mCalled = true;
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
}
}
@@ -124,118 +69,43 @@
*
* @param message error message to be displayed to the user.
*/
- public void onFailure(CharSequence message) {
- if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
-
- Preconditions.checkArgument(message != null, "message cannot be null");
-
- synchronized (mCallback) {
- assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
- | STATE_WAITING_DATASET_AUTH_RESPONSE);
-
- try {
- mCallback.showError(message);
- setFinalStateLocked(STATE_FINISHED_FAILURE);
- } catch (RemoteException e) {
- setFinalStateLocked(STATE_FINISHED_ERROR);
- e.rethrowAsRuntimeException();
- }
+ public void onFailure(@Nullable CharSequence message) {
+ assertNotCalled();
+ mCalled = true;
+ try {
+ mCallback.onFailure(message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
}
}
- /**
- * Notifies the Android System when the user authenticated a {@link FillResponse} previously
- * passed to {@link #onSuccess(FillResponse)}.
- *
- * @param flags must contain either
- * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
- * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
- */
- public void onFillResponseAuthentication(int flags) {
- if (DEBUG) Log.d(TAG, "onFillResponseAuthentication(): flags=" + flags);
-
- synchronized (mCallback) {
- assertOnStateLocked(STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE);
-
- try {
- mCallback.unlockFillResponse(flags);
- setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
- } catch (RemoteException e) {
- setFinalStateLocked(STATE_FINISHED_ERROR);
- e.rethrowAsRuntimeException();
- }
- }
- }
-
- /**
- * Notifies the Android System when the user authenticated a {@link Dataset} previously passed
- * to {@link #onSuccess(FillResponse)}.
- *
- * @param dataset values to fill the activity with in case of successful authentication of a
- * previously locked (and empty) dataset).
- * @param flags must contain either
- * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
- * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
- */
- public void onDatasetAuthentication(@Nullable Dataset dataset, int flags) {
- if (DEBUG) Log.d(TAG, "onDatasetAuthentication(): dataset=" + dataset + ", flags=" + flags);
-
- synchronized (mCallback) {
- assertOnStateLocked(STATE_WAITING_DATASET_AUTH_RESPONSE);
-
- try {
- mCallback.unlockDataset(dataset, flags);
- setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
- } catch (RemoteException e) {
- setFinalStateLocked(STATE_FINISHED_ERROR);
- e.rethrowAsRuntimeException();
- }
- }
- }
-
- @Override
- public String toString() {
- if (!DEBUG) return super.toString();
-
- return "FillCallback: [mState = " + mState + "]";
- }
-
/** @hide */
@Override
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("FillCallback: mState="); pw.println(mState);
+ public int describeContents() {
+ return 0;
}
/** @hide */
@Override
- public void setFinalizer(Finalizer f) {
- synchronized (mCallback) {
- mFinalizer = f;
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeStrongBinder(mCallback.asBinder());
+ }
+
+ private void assertNotCalled() {
+ if (mCalled) {
+ throw new IllegalStateException("Already called");
}
}
- /**
- * Sets a final state (where the callback cannot be used anymore) and notifies the
- * {@link Finalizer} (if any).
- */
- private void setFinalStateLocked(int state) {
- if (DEBUG) Log.d(TAG, "setFinalState(): " + state);
- mState = state;
-
- if (mFinalizer != null) {
- mFinalizer.gone();
+ public static final Creator<FillCallback> CREATOR = new Creator<FillCallback>() {
+ @Override
+ public FillCallback createFromParcel(Parcel parcel) {
+ return new FillCallback(parcel);
}
- }
- // TODO(b/33197203): move and/or re-add state check logic on server side to avoid malicious app
- // calling the callback on wrong state.
-
- // Make sure callback method is called during the proper lifecycle state.
- private void assertOnStateLocked(int flags) {
- if (DEBUG) Log.d(TAG, "assertOnState(): current=" + mState + ", required=" + flags);
-
- Preconditions.checkState((flags & mState) != 0,
- "invalid state: required " + flagsToString(FillCallback.class, "STATE_", flags)
- + ", current is " + flagsToString(FillCallback.class, "STATE_", mState));
- }
+ @Override
+ public FillCallback[] newArray(int size) {
+ return new FillCallback[size];
+ }
+ };
}
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index 8c3898a..d9c161c 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -18,6 +18,8 @@
import java.util.List;
+import android.content.Intent;
+import android.content.IntentSender;
import android.view.autofill.Dataset;
/**
@@ -31,4 +33,9 @@
* Auto-fills the activity with the contents of a dataset.
*/
void autoFill(in Dataset dataset);
+
+ /**
+ * Start an intent sender from the context of the filled app
+ */
+ void startIntentSender(in IntentSender intent, in Intent fillInIntent);
}
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
deleted file mode 100644
index 480438a..0000000
--- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl
+++ /dev/null
@@ -1,39 +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 java.util.List;
-
-import android.os.Bundle;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.Dataset;
-import android.view.autofill.FillResponse;
-
-/**
- * Object running in the AutoFillService process and used to communicate back with system_server.
- *
- * @hide
- */
-// TODO(b/33197203): rename to IAutoFillServerSession
-oneway interface IAutoFillServerCallback {
- // TODO(b/33197203): document methods
- void showResponse(in FillResponse response);
- void showError(CharSequence message);
- void onSaved();
- void unlockFillResponse(int flags);
- void unlockDataset(in Dataset dataset, int flags);
-}
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index a4e6ebc..fa1ea65 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -18,19 +18,20 @@
import android.app.assist.AssistStructure;
import android.os.Bundle;
-import android.service.autofill.IAutoFillServerCallback;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.ISaveCallback;
import com.android.internal.os.IResultReceiver;
/**
+ * Interface from the system to an auto fill service.
+ *
* @hide
*/
-// TODO(b/33197203): document class and methods
oneway interface IAutoFillService {
- // TODO(b/33197203): rename method to make them more consistent
- void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback);
- void save(in AssistStructure structure, in IAutoFillServerCallback callback, in Bundle extras);
- void authenticateFillResponse(in Bundle extras, int flags);
- void authenticateDataset(in Bundle extras, int flags);
+ void onFillRequest(in AssistStructure structure, in Bundle extras,
+ in IFillCallback callback);
+ void onSaveRequest(in AssistStructure structure, in Bundle extras,
+ in ISaveCallback callback);
void onConnected();
void onDisconnected();
}
diff --git a/core/java/android/service/autofill/CallbackHelper.java b/core/java/android/service/autofill/IFillCallback.aidl
similarity index 66%
copy from core/java/android/service/autofill/CallbackHelper.java
copy to core/java/android/service/autofill/IFillCallback.aidl
index ded8f97..537403e 100644
--- a/core/java/android/service/autofill/CallbackHelper.java
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -13,18 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.service.autofill;
-import java.io.PrintWriter;
+import android.os.ICancellationSignal;
-final class CallbackHelper {
+import android.view.autofill.FillResponse;
- static interface Dumpable {
- void dump(String prefix, PrintWriter pw);
- void setFinalizer(Finalizer f);
- }
-
- static interface Finalizer {
- void gone();
- }
+/**
+ * Interface to receive the result of a save request.
+ *
+ * @hide
+ */
+interface IFillCallback {
+ void onCancellable(in ICancellationSignal cancellation);
+ void onSuccess(in FillResponse response);
+ void onFailure(CharSequence message);
}
diff --git a/core/java/android/service/autofill/CallbackHelper.java b/core/java/android/service/autofill/ISaveCallback.aidl
similarity index 72%
rename from core/java/android/service/autofill/CallbackHelper.java
rename to core/java/android/service/autofill/ISaveCallback.aidl
index ded8f97..e260c73 100644
--- a/core/java/android/service/autofill/CallbackHelper.java
+++ b/core/java/android/service/autofill/ISaveCallback.aidl
@@ -13,18 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.service.autofill;
-import java.io.PrintWriter;
-
-final class CallbackHelper {
-
- static interface Dumpable {
- void dump(String prefix, PrintWriter pw);
- void setFinalizer(Finalizer f);
- }
-
- static interface Finalizer {
- void gone();
- }
+/**
+ * Interface to receive the result of a save request.
+ *
+ * @hide
+ */
+interface ISaveCallback {
+ void onSuccess();
+ void onFailure(CharSequence message);
}
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 9dd9795..46b3072 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -16,21 +16,9 @@
package android.service.autofill;
-import static android.service.autofill.AutoFillService.DEBUG;
-
import android.app.Activity;
-import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
import android.os.RemoteException;
-import android.service.autofill.CallbackHelper.Dumpable;
-import android.service.autofill.CallbackHelper.Finalizer;
-import android.util.Log;
-import android.view.autofill.AutoFillId;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
/**
* Handles save requests from the {@link AutoFillService} into the {@link Activity} being
@@ -38,20 +26,12 @@
*
* <p>This class is thread safe.
*/
-public final class SaveCallback implements Dumpable {
-
- private static final String TAG = "SaveCallback";
-
- private final IAutoFillServerCallback mCallback;
-
- @GuardedBy("mCallback")
- private boolean mReplied = false;
-
- @GuardedBy("mCallback")
- private Finalizer mFinalizer;
+public final class SaveCallback {
+ private final ISaveCallback mCallback;
+ private boolean mCalled;
/** @hide */
- SaveCallback(IAutoFillServerCallback callback) {
+ SaveCallback(ISaveCallback callback) {
mCallback = callback;
}
@@ -63,17 +43,12 @@
* @throws RuntimeException if an error occurred while calling the Android System.
*/
public void onSuccess() {
- if (DEBUG) Log.d(TAG, "onSuccess()");
-
- synchronized (mCallback) {
- checkNotRepliedYetLocked();
- try {
- mCallback.onSaved();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- } finally {
- setRepliedLocked();
- }
+ assertNotCalled();
+ mCalled = true;
+ try {
+ mCallback.onSuccess();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
}
}
@@ -87,54 +62,18 @@
* @throws RuntimeException if an error occurred while calling the Android System.
*/
public void onFailure(CharSequence message) {
- if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
-
- synchronized (mCallback) {
- checkNotRepliedYetLocked();
-
- try {
- mCallback.showError(message);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- } finally {
- setRepliedLocked();
- }
+ assertNotCalled();
+ mCalled = true;
+ try {
+ mCallback.onFailure(message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
}
}
- /** @hide */
- @Override
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("SaveCallback: mReplied="); pw.println(mReplied);
- }
-
- /** @hide */
- @Override
- public void setFinalizer(Finalizer f) {
- synchronized (mCallback) {
- mFinalizer = f;
- }
- }
-
- @Override
- public String toString() {
- if (!DEBUG) return super.toString();
-
- return "SaveCallback: [mReplied= " + mReplied + "]";
- }
-
- // There can be only one!!
- private void checkNotRepliedYetLocked() {
- Preconditions.checkState(!mReplied, "already replied");
- }
-
- private void setRepliedLocked() {
- if (DEBUG) Log.d(TAG, "setReplied()");
-
- mReplied = true;
-
- if (mFinalizer != null) {
- mFinalizer.gone();
+ private void assertNotCalled() {
+ if (mCalled) {
+ throw new IllegalStateException("Already called");
}
}
}
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 887f4b6..1781c2a 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -130,6 +130,11 @@
*/
public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_STATE = "state";
+
private final H mHandler = new H(Looper.getMainLooper());
private boolean mListening = false;
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index 62ecab3..10e4177 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -42,5 +42,13 @@
*/
boolean getVrModeState();
+ /**
+ * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
+ * remain in VR mode even if the foreground does not specify VR mode being enabled. Mainly used
+ * by VR viewers to indicate that a device is placed in a VR viewer.
+ *
+ * @param enabled true if the device should be placed in persistent VR mode.
+ */
+ void setPersistentVrModeEnabled(in boolean enabled);
}
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index 4b0b065..584c114 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -465,7 +465,7 @@
}
}
- private static class GhostListener extends Transition.TransitionListenerAdapter {
+ private static class GhostListener extends TransitionListenerAdapter {
private View mView;
private View mStartView;
private GhostView mGhostView;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 8823605..af2547e 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -2342,34 +2342,6 @@
}
/**
- * Utility adapter class to avoid having to override all three methods
- * whenever someone just wants to listen for a single event.
- *
- * @hide
- * */
- public static class TransitionListenerAdapter implements TransitionListener {
- @Override
- public void onTransitionStart(Transition transition) {
- }
-
- @Override
- public void onTransitionEnd(Transition transition) {
- }
-
- @Override
- public void onTransitionCancel(Transition transition) {
- }
-
- @Override
- public void onTransitionPause(Transition transition) {
- }
-
- @Override
- public void onTransitionResume(Transition transition) {
- }
- }
-
- /**
* Holds information about each animator used when a new transition starts
* while other transitions are still running to determine whether a running
* animation should be canceled or a new animation noop'd. The structure holds
diff --git a/core/java/android/transition/TransitionListenerAdapter.java b/core/java/android/transition/TransitionListenerAdapter.java
new file mode 100644
index 0000000..c217949
--- /dev/null
+++ b/core/java/android/transition/TransitionListenerAdapter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.transition;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link
+ * android.transition.Transition.TransitionListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public abstract class TransitionListenerAdapter implements Transition.TransitionListener {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransitionPause(Transition transition) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransitionResume(Transition transition) {
+ }
+}
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 6e4d78d..325ff38 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -298,7 +298,7 @@
previousRunningTransitions = new ArrayList<Transition>(currentTransitions);
}
currentTransitions.add(mTransition);
- mTransition.addListener(new Transition.TransitionListenerAdapter() {
+ mTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
ArrayList<Transition> currentTransitions =
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 0da710a..c3d3f39 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Outline;
@@ -39,7 +40,7 @@
*/
@RemoteViews.RemoteView
public class NotificationHeaderView extends ViewGroup {
- public static final int NO_COLOR = -1;
+ public static final int NO_COLOR = Notification.COLOR_INVALID;
private final int mChildMinWidth;
private final int mContentEndMargin;
private View mAppName;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e2eee95..a150529 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19942,9 +19942,9 @@
* @return the view of the specified id, null if cannot be found
* @hide
*/
- protected <T extends View> T findViewTraversal(@IdRes int id) {
+ protected View findViewTraversal(@IdRes int id) {
if (id == mID) {
- return (T) this;
+ return this;
}
return null;
}
@@ -19954,9 +19954,9 @@
* @return the view of specified tag, null if cannot be found
* @hide
*/
- protected <T extends View> T findViewWithTagTraversal(Object tag) {
+ protected View findViewWithTagTraversal(Object tag) {
if (tag != null && tag.equals(mTag)) {
- return (T) this;
+ return this;
}
return null;
}
@@ -19967,10 +19967,9 @@
* @return The first view that matches the predicate or null.
* @hide
*/
- protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
- View childToSkip) {
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
if (predicate.apply(this)) {
- return (T) this;
+ return this;
}
return null;
}
@@ -19983,7 +19982,7 @@
* @return The view that has the given id in the hierarchy or null
*/
@Nullable
- public final <T extends View> T findViewById(@IdRes int id) {
+ public final View findViewById(@IdRes int id) {
if (id < 0) {
return null;
}
@@ -19996,11 +19995,11 @@
* @param accessibilityId The searched accessibility id.
* @return The found view.
*/
- final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
+ final View findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId < 0) {
return null;
}
- T view = findViewByAccessibilityIdTraversal(accessibilityId);
+ View view = findViewByAccessibilityIdTraversal(accessibilityId);
if (view != null) {
return view.includeForAccessibility() ? view : null;
}
@@ -20019,11 +20018,12 @@
*
* @param accessibilityId The accessibility id.
* @return The found view.
+ *
* @hide
*/
- public <T extends View> T findViewByAccessibilityIdTraversal(int accessibilityId) {
+ public View findViewByAccessibilityIdTraversal(int accessibilityId) {
if (getAccessibilityViewId() == accessibilityId) {
- return (T) this;
+ return this;
}
return null;
}
@@ -20035,7 +20035,7 @@
* @param tag The tag to search for, using "tag.equals(getTag())".
* @return The View that has the given tag in the hierarchy or null
*/
- public final <T extends View> T findViewWithTag(Object tag) {
+ public final View findViewWithTag(Object tag) {
if (tag == null) {
return null;
}
@@ -20050,7 +20050,7 @@
* @return The first view that matches the predicate or null.
* @hide
*/
- public final <T extends View> T findViewByPredicate(Predicate<View> predicate) {
+ public final View findViewByPredicate(Predicate<View> predicate) {
return findViewByPredicateTraversal(predicate, null);
}
@@ -20070,11 +20070,10 @@
* @return The first view that matches the predicate or null.
* @hide
*/
- public final <T extends View> T findViewByPredicateInsideOut(
- View start, Predicate<View> predicate) {
+ public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) {
View childToSkip = null;
for (;;) {
- T view = start.findViewByPredicateTraversal(predicate, childToSkip);
+ View view = start.findViewByPredicateTraversal(predicate, childToSkip);
if (view != null || start == this) {
return view;
}
@@ -24660,7 +24659,7 @@
* Determine if this view is rendered on a round wearable device and is the main view
* on the screen.
*/
- private boolean shouldDrawRoundScrollbar() {
+ boolean shouldDrawRoundScrollbar() {
if (!mResources.getConfiguration().isScreenRound() || mAttachInfo == null) {
return false;
}
@@ -24677,7 +24676,7 @@
return false;
}
- getLocationOnScreen(mAttachInfo.mTmpLocation);
+ getLocationInWindow(mAttachInfo.mTmpLocation);
return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft()
&& mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b11b3d7..aa580fa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4208,9 +4208,9 @@
* {@hide}
*/
@Override
- protected <T extends View> T findViewTraversal(@IdRes int id) {
+ protected View findViewTraversal(@IdRes int id) {
if (id == mID) {
- return (T) this;
+ return this;
}
final View[] where = mChildren;
@@ -4223,7 +4223,7 @@
v = v.findViewById(id);
if (v != null) {
- return (T) v;
+ return v;
}
}
}
@@ -4235,9 +4235,9 @@
* {@hide}
*/
@Override
- protected <T extends View> T findViewWithTagTraversal(Object tag) {
+ protected View findViewWithTagTraversal(Object tag) {
if (tag != null && tag.equals(mTag)) {
- return (T) this;
+ return this;
}
final View[] where = mChildren;
@@ -4250,7 +4250,7 @@
v = v.findViewWithTag(tag);
if (v != null) {
- return (T) v;
+ return v;
}
}
}
@@ -4262,10 +4262,9 @@
* {@hide}
*/
@Override
- protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
- View childToSkip) {
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
if (predicate.apply(this)) {
- return (T) this;
+ return this;
}
final View[] where = mChildren;
@@ -4278,7 +4277,7 @@
v = v.findViewByPredicate(predicate);
if (v != null) {
- return (T) v;
+ return v;
}
}
}
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index f2f522d..147d72a 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -16,6 +16,8 @@
package android.view.autofill;
+import static android.view.autofill.Helper.DEBUG;
+
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
@@ -31,7 +33,6 @@
public final class AutoFillManager {
private static final String TAG = "AutoFillManager";
- private static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
/**
* Flag used to show the auto-fill UI affordance for a view.
diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java
index eec7a82..efc1df6 100644
--- a/core/java/android/view/autofill/AutoFillSession.java
+++ b/core/java/android/view/autofill/AutoFillSession.java
@@ -19,7 +19,8 @@
import static android.view.autofill.Helper.DEBUG;
import android.app.Activity;
-import android.os.RemoteException;
+import android.content.Intent;
+import android.content.IntentSender;
import android.service.autofill.IAutoFillAppCallback;
import android.util.Log;
import android.view.View;
@@ -39,7 +40,7 @@
private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() {
@Override
- public void autoFill(Dataset dataset) throws RemoteException {
+ public void autoFill(Dataset dataset) {
final Activity activity = mActivity.get();
if (activity == null) {
if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed");
@@ -49,12 +50,10 @@
// dataset.extras to service
activity.runOnUiThread(() -> {
final View root = activity.getWindow().getDecorView().getRootView();
- for (DatasetField field : dataset.getFields()) {
- final AutoFillId id = field.getId();
- if (id == null) {
- Log.w(TAG, "autoFill(): null id on " + field);
- continue;
- }
+ final int itemCount = dataset.getFieldIds().size();
+ for (int i = 0; i < itemCount; i++) {
+ final AutoFillId id = dataset.getFieldIds().get(i);
+ final AutoFillValue value = dataset.getFieldValues().get(i);
final int viewId = id.getViewId();
final View view = root.findViewByAccessibilityIdTraversal(viewId);
if (view == null) {
@@ -79,14 +78,28 @@
Log.d(TAG, "autoFill(): delegating " + id
+ " to VirtualViewDelegate " + delegate);
}
- delegate.autoFill(id.getVirtualChildId(), field.getValue());
+ delegate.autoFill(id.getVirtualChildId(), value);
} else {
// Handle non-virtual fields itself.
- view.autoFill(field.getValue());
+ view.autoFill(value);
}
}
});
}
+
+ @Override
+ public void startIntentSender(IntentSender intent, Intent fillInIntent) {
+ final Activity activity = mActivity.get();
+ if (activity != null) {
+ activity.runOnUiThread(() -> {
+ try {
+ activity.startIntentSender(intent, fillInIntent, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.e(TAG, "startIntentSender() failed for intent:" + intent, e);
+ }
+ });
+ }
+ }
};
private final WeakReference<Activity> mActivity;
@@ -114,5 +127,4 @@
}
}
}
-
}
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
index 18a08f9..2708358 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/view/autofill/Dataset.java
@@ -17,12 +17,12 @@
package android.view.autofill;
import static android.view.autofill.Helper.DEBUG;
-import static android.view.autofill.Helper.append;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.assist.AssistStructure.ViewNode;
-import android.hardware.fingerprint.FingerprintManager.CryptoObject;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,8 +30,6 @@
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}.
@@ -44,221 +42,184 @@
* <li>An optional {@link Bundle} with extras (used only by the service creating it).
* </ol>
*
- * See {@link FillResponse} for examples.
+ * @see FillResponse for examples.
*/
public final class Dataset implements Parcelable {
-
+ private final String mId;
private final CharSequence mName;
- private final ArrayList<DatasetField> mFields;
+ private final ArrayList<AutoFillId> mFieldIds;
+ private final ArrayList<AutoFillValue> mFieldValues;
private final Bundle mExtras;
- private final int mFlags;
- private final boolean mRequiresAuth;
- private final boolean mHasCryptoObject;
- private final long mCryptoOpId;
+ private final IntentSender mAuthentication;
- private Dataset(Dataset.Builder builder) {
+ private Dataset(Builder builder) {
+ mId = builder.mId;
mName = builder.mName;
- // TODO(b/33197203): make an immutable copy of mFields?
- mFields = builder.mFields;
+ mFieldIds = builder.mFieldIds;
+ mFieldValues = builder.mFieldValues;
mExtras = builder.mExtras;
- mFlags = builder.mFlags;
- mRequiresAuth = builder.mRequiresAuth;
- mHasCryptoObject = builder.mHasCryptoObject;
- mCryptoOpId = builder.mCryptoOpId;
+ mAuthentication = builder.mAuthentication;
}
/** @hide */
- public CharSequence getName() {
+ public @NonNull String getId() {
+ return mId;
+ }
+
+ /** @hide */
+ public @NonNull CharSequence getName() {
return mName;
}
/** @hide */
- public List<DatasetField> getFields() {
- return mFields;
+ public @Nullable ArrayList<AutoFillId> getFieldIds() {
+ return mFieldIds;
}
/** @hide */
- public Bundle getExtras() {
+ public @Nullable ArrayList<AutoFillValue> getFieldValues() {
+ return mFieldValues;
+ }
+
+ /** @hide */
+ public @Nullable Bundle getExtras() {
return mExtras;
}
/** @hide */
- public int getFlags() {
- return mFlags;
- }
-
- /** @hide */
- public boolean isAuthRequired() {
- return mRequiresAuth;
+ public @Nullable IntentSender getAuthentication() {
+ return mAuthentication;
}
/** @hide */
public boolean isEmpty() {
- return mFields.isEmpty();
- }
-
- /** @hide */
- public boolean hasCryptoObject() {
- return mHasCryptoObject;
- }
-
- /** @hide */
- public long getCryptoObjectOpId() {
- return mCryptoOpId;
+ return mFieldIds == null || mFieldIds.isEmpty();
}
@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)
- .append(", flags=").append(mFlags)
- .append(", requiresAuth: ").append(mRequiresAuth)
- .append(", hasCrypto: ").append(mHasCryptoObject);
+ final StringBuilder builder = new StringBuilder("Dataset [id=").append(mId)
+ .append(", name=").append(mName)
+ .append(", fieldIds=").append(mFieldIds)
+ .append(", fieldValues=").append(mFieldValues)
+ .append(", hasAuthentication=").append(mAuthentication != null)
+ .append(", hasExtras=").append(mExtras != null);
return builder.append(']').toString();
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Dataset other = (Dataset) obj;
+ if (mId == null) {
+ if (other.mId != null) {
+ return false;
+ }
+ } else if (!mId.equals(other.mId)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mId != null ? mId.hashCode() : 0;
+ }
+
/**
- * A builder for {@link Dataset} objects.
+ * A builder for {@link Dataset} objects. You must to provide at least
+ * one value for a field or set an authentication intent.
*/
public static final class Builder {
+ private String mId;
private CharSequence mName;
- private final ArrayList<DatasetField> mFields = new ArrayList<>();
+ private ArrayList<AutoFillId> mFieldIds;
+ private ArrayList<AutoFillValue> mFieldValues;
private Bundle mExtras;
- private int mFlags;
- private boolean mRequiresAuth;
- private boolean mHasCryptoObject;
- private long mCryptoOpId;
+ private IntentSender mAuthentication;
+ private boolean mDestroyed;
+
+ /** @hide */
+ // TODO(b/33197203): Remove once GCore migrates
+ public Builder(@NonNull CharSequence name) {
+ this(String.valueOf(System.currentTimeMillis()), name);
+ }
/**
* Creates a new builder.
*
+ * @param id A required id to identify this dataset for future interactions related to it.
* @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
+ * the first field in the dataset (like username or email address) or a user-provided name
* (like "My Work Address").
*/
- public Builder(CharSequence name) {
+ public Builder(@NonNull String id, @NonNull CharSequence name) {
+ mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null");
}
/**
- * Requires dataset authentication through the {@link
- * android.service.autofill.AutoFillService} before auto-filling the activity with this
- * dataset.
+ * Requires a dataset authentication before auto-filling the activity with this dataset.
*
- * <p>This method is typically called when the device (or the service) does not support
- * fingerprint authentication (and hence it cannot use {@link
- * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
- * to use a custom authentication UI for the dataset. For example, when a dataset contains
- * credit card information (such as number, expiration date, and verification code), the
- * service displays an authentication dialog asking for the verification code to unlock the
- * rest of the data).
+ * <p>This method is called when you need to provide an authentication
+ * UI for the dataset. For example, when a dataset contains credit card information
+ * (such as number, expiration date, and verification code), you can display UI
+ * asking for the verification code to before filing in the data). Even if the
+ * dataset is completely populated the system will launch the specified authentication
+ * intent and will need your approval to fill it in. Since the dataset is "locked"
+ * until the user authenticates it, typically this dataset name is masked
+ * (for example, "VISA....1234"). Typically you would want to store the dataset
+ * labels non-encypted and the actual sensitive data encrypted and not in memory.
+ * This allows showing the labels in the UI while involving the user if one of
+ * the items with these labels is chosen. Note that if you use sensitive data as
+ * a label, for example an email address, then it should also be encrypted.
+ *</p>
*
- * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
- * name is masked (for example, "VISA....1234").
+ * <p>When a user selects this dataset, the system triggers the provided intent
+ * whose extras will have the {@link android.content.Intent#EXTRA_AUTO_FILL_ITEM_ID id}
+ * of the {@link android.view.autofill.Dataset dataset} to authenticate, the {@link
+ * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} associated with this
+ * dataset, and a {@link android.content.Intent#EXTRA_AUTO_FILL_CALLBACK callback}
+ * to dispatch the authentication result.</p>
*
- * <p>When the user selects this dataset, the Android System calls {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
- * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in
- * the flags and the same {@code extras} passed to this method. The service can then
- * displays its custom authentication UI, and then call the proper method on {@link
- * android.service.autofill.FillCallback} depending on the authentication result and whether
- * this dataset already contains the fields needed to auto-fill the activity:
+ * <p>Once you complete your authentication flow you should use the provided callback
+ * to notify for a failure or a success. In case of a success you need to provide
+ * only the fully populated dataset that is being authenticated. For example, if you
+ * provided a {@link FillResponse} with two {@link Dataset}s and marked that
+ * only the first dataset needs an authentication then in the provided response
+ * you need to provide only the fully populated dataset being authenticated instead
+ * of both of them.
+ * </p>
*
- * <ul>
- * <li>If authentication failed, call
- * {@link android.service.autofill.FillCallback#onDatasetAuthentication(Dataset,
- * int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
- * <li>If authentication succeeded and this datast is empty (no fields), call {@link
- * android.service.autofill.FillCallback#onSuccess(FillResponse)} with a new dataset
- * (with the proper fields).
- * <li>If authentication succeeded and this response is not empty, call {@link
- * android.service.autofill.FillCallback#onDatasetAuthentication(Dataset, int)}
- * passing
- * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the
- * {@code flags} and {@code null} as the {@code dataset}.
- * </ul>
+ * <p>The indent sender mechanism allows you to have your authentication UI
+ * implemented as an activity or a service or a receiver. However, the recommended
+ * way is to do this is with an activity which the system will start in the
+ * filled activity's task meaning it will properly work with back, recent apps, and
+ * free-form multi-window, while avoiding the need for the "draw on top of other"
+ * apps special permission. You can still theme your authentication activity's
+ * UI to look like a dialog if desired.</p>
*
- * @param extras when set, will be passed back in the {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
- * int)}, call so it could be used by the service to handle state.
- * @param flags optional parameters, currently ignored.
+ * <p></><strong>Note:</strong> Do not make the provided intent sender
+ * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+ * platform needs to fill in the authentication arguments.</p>
+ *
+ * @param authentication Intent to trigger your authentication flow.
+ *
+ * @see android.app.PendingIntent#getIntentSender()
*/
- public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
- return requiresAuthentication(null, extras, flags);
- }
-
- /**
- * Requires dataset authentication through the Fingerprint sensor before auto-filling the
- * activity with this dataset.
- *
- * <p>This method is typically called when the dataset contains sensitive information (for
- * example, credit card information) and the provider requires the user to re-authenticate
- * before using it.
- *
- * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
- * name is masked (for example, "VISA....1234").
- *
- * <p>When the user selects this dataset, the Android System displays an UI affordance
- * asking the user to use the fingerprint sensor unlock the dataset, and what happens after
- * a successful fingerprint authentication depends on whether the dataset is empty (no
- * fields, only the masked name) or not:
- *
- * <ul>
- * <li>If it's empty, the Android System will call {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
- * int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
- * flags.
- * <li>If it's not empty, the activity will be auto-filled with its data.
- * </ul>
- *
- * <p>If the fingerprint authentication fails, the Android System will call {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
- * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the
- * flags.
- *
- * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
- * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
- * authentication is available before using this method, and use other alternatives (such as
- * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
- * called when fingerprint is not available, Android System will call {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
- * passing {@link
- * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
- * in the flags, but it would be wasting system resources (and worsening the user
- * experience) in the process.
- *
- * @param crypto object that will be authenticated.
- * @param extras when set, will be passed back in the {@link
- * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
- * call so it could be used by the service to handle state.
- * @param flags optional parameters, currently ignored.
- */
- public Builder requiresFingerprintAuthentication(CryptoObject crypto,
- @Nullable Bundle extras, int flags) {
- // TODO(b/33197203): should we allow crypto to be null?
- Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
- return requiresAuthentication(crypto, extras, flags);
- }
-
- private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
- int flags) {
- // There can be only one!
- Preconditions.checkState(!mRequiresAuth,
- "requires-authentication methods already called");
- // TODO(b/33197203): make sure that either this method or setExtras() is called, but
- // not both
- mExtras = extras;
- mFlags = flags;
- mRequiresAuth = true;
- if (cryptoObject != null) {
- mHasCryptoObject = true;
- mCryptoOpId = cryptoObject.getOpId();
- }
+ public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
+ throwIfDestroyed();
+ mAuthentication = authentication;
return this;
}
@@ -268,41 +229,55 @@
* @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));
+ public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
+ throwIfDestroyed();
+ Preconditions.checkNotNull(id, "id cannot be null");
+ Preconditions.checkNotNull(value, "value cannot be null");
+ if (mFieldIds != null) {
+ final int existingIdx = mFieldIds.indexOf(id);
+ if (existingIdx >= 0) {
+ mFieldValues.set(existingIdx, value);
+ return this;
+ }
+ } else {
+ mFieldIds = new ArrayList<>();
+ mFieldValues = new ArrayList<>();
+ }
+ mFieldIds.add(id);
+ mFieldValues.add(value);
return this;
}
/**
- * Creates a new {@link Dataset} instance.
+ * Sets a {@link Bundle} that will be passed to subsequent APIs that
+ * manipulate this dataset. For example, they are passed in as {@link
+ * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
+ * authentication flow.
*/
- public Dataset build() {
+ public @NonNull Builder setExtras(@Nullable Bundle extras) {
+ throwIfDestroyed();
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link Dataset} instance. You should not interact
+ * with this builder once this method is called.
+ */
+ public @NonNull Dataset build() {
+ throwIfDestroyed();
+ mDestroyed = true;
+ if (mFieldIds == null && mAuthentication == null) {
+ throw new IllegalArgumentException(
+ "at least one value or an authentication must be set");
+ }
return new Dataset(this);
}
- /**
- * Sets a {@link Bundle} that will be passed to subsequent calls to
- * {@link android.service.autofill.AutoFillService} methods such as
- * {@link android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.service.autofill.SaveCallback)}, using
- * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
- *
- * <p>It can be used to keep service state in between calls.
- */
- public Builder setExtras(Bundle extras) {
- // TODO(b/33197203): make sure that either this method or the requires-Authentication
- // ones are called, but not both
- 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);
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Already called #build()");
+ }
}
}
@@ -317,32 +292,33 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mId);
parcel.writeCharSequence(mName);
- parcel.writeList(mFields);
+ parcel.writeTypedArrayList(mFieldIds, 0);
+ parcel.writeTypedArrayList(mFieldValues, 0);
parcel.writeBundle(mExtras);
- parcel.writeInt(mFlags);
- parcel.writeInt(mRequiresAuth ? 1 : 0);
- parcel.writeInt(mHasCryptoObject ? 1 : 0);
- if (mHasCryptoObject) {
- parcel.writeLong(mCryptoOpId);
- }
- }
-
- @SuppressWarnings("unchecked")
- private Dataset(Parcel parcel) {
- mName = parcel.readCharSequence();
- mFields = parcel.readArrayList(null);
- mExtras = parcel.readBundle();
- mFlags = parcel.readInt();
- mRequiresAuth = parcel.readInt() == 1;
- mHasCryptoObject = parcel.readInt() == 1;
- mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
+ parcel.writeParcelable(mAuthentication, flags);
}
public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() {
@Override
- public Dataset createFromParcel(Parcel source) {
- return new Dataset(source);
+ public Dataset createFromParcel(Parcel parcel) {
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final Builder builder = new Builder(parcel.readString(), parcel.readCharSequence());
+ final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null);
+ final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null);
+ final int idCount = (ids != null) ? ids.size() : 0;
+ final int valueCount = (values != null) ? values.size() : 0;
+ for (int i = 0; i < idCount; i++) {
+ AutoFillId id = ids.get(i);
+ AutoFillValue value = (valueCount > i) ? values.get(i) : null;
+ builder.setValue(id, value);
+ }
+ builder.setExtras(parcel.readBundle());
+ builder.setAuthentication(parcel.readParcelable(null));
+ return builder.build();
}
@Override
diff --git a/core/java/android/view/autofill/DatasetField.java b/core/java/android/view/autofill/DatasetField.java
deleted file mode 100644
index c6b92ac..0000000
--- a/core/java/android/view/autofill/DatasetField.java
+++ /dev/null
@@ -1,86 +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.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/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
index 48dbb84..596a06c 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -16,36 +16,30 @@
package android.view.autofill;
import static android.view.autofill.Helper.DEBUG;
-import static android.view.autofill.Helper.append;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
-import android.hardware.fingerprint.FingerprintManager.CryptoObject;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.service.autofill.FillCallback;
+import android.util.ArraySet;
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
* android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} request.
+ * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} and
+ * authentication requests.
*
* <p>The response typically contains one or more {@link Dataset}s, each representing a set of
- * fields that can be auto-filled together, and the Android System displays a dataset picker UI
+ * fields that can be auto-filled together, and the Android system displays a dataset picker UI
* affordance that the user must use before the {@link Activity} is filled with the dataset.
*
* <p>For example, for a login page with username/password where the user only has one account in
- * the service, the response could be:
+ * the response could be:
*
* <pre class="prettyprint">
* new FillResponse.Builder()
@@ -102,12 +96,17 @@
* <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:
+ * <p>If the service has multiple {@link Dataset}s for different sections of the activity,
+ * for example, a user section for which there are two datasets followed by an address
+ * section for which there are two datasets for each user user, then it should "partition"
+ * the activity in sections and populate the response with just a subset of the data that would
+ * fulfill the first section (the name in our example); then once the user fills the first
+ * section and taps a field from the next section (the address in our example), the Android
+ * system would issue another request for that section, and so on. Note that if the user
+ * chooses to populate the first section with a service provided dataset, the subsequent request
+ * would contain the populated values so you don't try to provide suggestions for the first
+ * section but ony for the second one based on the context of what was already filled. For
+ * example, the first response could be:
*
* <pre class="prettyprint">
* new FillResponse.Builder()
@@ -132,236 +131,182 @@
* .setTextFieldValue(id4, "Springfield")
* .build())
* .add(new Dataset.Builder("Work")
- * .setTextFieldValue(id3, "Springfield Nuclear Power Plant")
+ * .setTextFieldValue(id3, "Springfield Power Plant")
* .setTextFieldValue(id4, "Springfield")
* .build())
* .build();
* </pre>
*
- * <p>The service could require user authentication, either at the {@link FillResponse} or {@link
- * Dataset} levels, prior to auto-filling the activity - see {@link
- * FillResponse.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, {@link
- * FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, {@link
- * Dataset.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, and {@link
- * Dataset.Builder#requiresCustomAuthentication(Bundle, int)} for details.
+ * <p>The service could require user authentication at the {@link FillResponse} or the
+ * {@link Dataset} level, prior to auto-filling an activity - see {@link FillResponse.Builder
+ * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}.
+ * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
+ * which would allow you to provide the dataset names to the user and if they choose one
+ * them challenge the user to authenticate. For example, if the user has a home and a work
+ * address the Home and Work labels could be stored unencrypted as they don't have any sensitive
+ * data while the address data is in an encrypted storage. If the user chooses Home, then the
+ * platform will start your authentication flow. If you encrypt all data and require auth
+ * at the response level the user will have to interact with the fill UI to trigger a request
+ * for the datasets as they don't see Home and Work options which will trigger your auth
+ * flow and after successfully authenticating the user will be presented with the Home and
+ * Work options where they can pick one. Hence, you have flexibility how to implement your
+ * auth while storing labels non-encrypted and data encrypted provides a better user
+ * experience.</p>
*
- * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or {@link
- * Dataset.Builder#setExtras(Bundle)} methods to pass {@link Bundle}s with service-specific data use
- * to identify this response on future calls (like {@link
- * android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.service.autofill.SaveCallback)}) - such bundles will be available as the
- * {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} and
- * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
- * {@code extras} argument.
+ * <p>Finally, the service can use {@link Dataset.Builder#setExtras(Bundle)} methods
+ * to pass {@link Bundle extras} provided to all future calls related to a dataset,
+ * for example during authentication and saving.</p>
*/
public final class FillResponse implements Parcelable {
-
- private final List<Dataset> mDatasets;
- private final AutoFillId[] mSavableIds;
+ private final String mId;
+ private final ArraySet<Dataset> mDatasets;
+ private final ArraySet<AutoFillId> mSavableIds;
private final Bundle mExtras;
- private final int mFlags;
- private final boolean mRequiresAuth;
- private final boolean mHasCryptoObject;
- private final long mCryptoOpId;
+ private final IntentSender mAuthentication;
- private FillResponse(Builder builder) {
- // TODO(b/33197203): make it immutable?
+ private FillResponse(@NonNull Builder builder) {
+ mId = builder.mId;
mDatasets = builder.mDatasets;
- final int size = builder.mSavableIds.size();
- mSavableIds = new AutoFillId[size];
- int i = 0;
- for (AutoFillId id : builder.mSavableIds) {
- mSavableIds[i++] = id;
- }
+ mSavableIds = builder.mSavableIds;
mExtras = builder.mExtras;
- mFlags = builder.mFlags;
- mRequiresAuth = builder.mRequiresAuth;
- mHasCryptoObject = builder.mHasCryptoObject;
- mCryptoOpId = builder.mCryptoOpId;
+ mAuthentication = builder.mAuthentication;
}
/** @hide */
- public List<Dataset> getDatasets() {
- return mDatasets;
+ public @NonNull String getId() {
+ return mId;
}
/** @hide */
- public AutoFillId[] getSavableIds() {
- return mSavableIds;
- }
-
- /** @hide */
- public Bundle getExtras() {
+ public @Nullable Bundle getExtras() {
return mExtras;
}
/** @hide */
- public int getFlags() {
- return mFlags;
+ public @Nullable ArraySet<Dataset> getDatasets() {
+ return mDatasets;
}
/** @hide */
- public boolean isAuthRequired() {
- return mRequiresAuth;
+ public @Nullable ArraySet<AutoFillId> getSavableIds() {
+ return mSavableIds;
}
/** @hide */
- public boolean hasCryptoObject() {
- return mHasCryptoObject;
- }
-
- /** @hide */
- public long getCryptoObjectOpId() {
- return mCryptoOpId;
+ public @Nullable IntentSender getAuthentication() {
+ return mAuthentication;
}
/**
- * Builder for {@link FillResponse} objects.
+ * Builder for {@link FillResponse} objects. You must to provide at least
+ * one dataset or set an authentication intent.
*/
public static final class Builder {
- private final List<Dataset> mDatasets = new ArrayList<>();
- private final Set<AutoFillId> mSavableIds = new HashSet<>();
+ private final String mId;
+ private ArraySet<Dataset> mDatasets;
+ private ArraySet<AutoFillId> mSavableIds;
private Bundle mExtras;
- private int mFlags;
- private boolean mRequiresAuth;
- private boolean mHasCryptoObject;
- private long mCryptoOpId;
+ private IntentSender mAuthentication;
+ private boolean mDestroyed;
- /**
- * Requires user authentication through the {@link android.service.autofill.AutoFillService}
- * before handling an auto-fill request.
- *
- * <p>This method is typically called when the device (or the service) does not support
- * fingerprint authentication (and hence it cannot use {@link
- * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
- * to use a custom authentication UI and is used in 2 scenarios:
- *
- * <ol>
- * <li>When the user data is encrypted and the service must authenticate an object that
- * will be used to decrypt it.
- * <li>When the service already acquired the user data but wants to confirm the user's
- * identity before the activity is filled with it.
- * </ol>
- *
- * <p>When this method is called, the Android System displays an UI affordance asking the
- * user to tap it to auto-fill the activity; if the user taps it, the Android System calls
- * {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in the flags and
- * the same {@code extras} passed to this method. The service can then displays its custom
- * authentication UI, and then call the proper method on {@link FillCallback} depending on
- * the authentication result and whether this response already contains the {@link Dataset}s
- * need to auto-fill the activity:
- *
- * <ul>
- * <li>If authentication failed, call {@link
- * FillCallback#onFillResponseAuthentication(int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
- * <li>If authentication succeeded and this response is empty (no datasets), call {@link
- * FillCallback#onSuccess(FillResponse)} with a new dataset (that does not require
- * authentication).
- * <li>If authentication succeeded and this response is not empty, call {@link
- * FillCallback#onFillResponseAuthentication(int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the flags.
- * </ul>
- *
- * @param extras when set, will be passed back in the {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} call so it could be used by the service to handle state.
- * @param flags optional parameters, currently ignored.
- */
- public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
- return requiresAuthentication(null, extras, flags);
+ /** @hide */
+ // TODO(b/33197203): Remove once GCore migrates
+ public Builder() {
+ this(String.valueOf(System.currentTimeMillis()));
}
/**
- * Requires user authentication through the Fingerprint sensor before handling an auto-fill
- * request.
+ * Creates a new {@link FillResponse} builder.
*
- * <p>The {@link android.service.autofill.AutoFillService} typically uses this method in 2
- * situations:
- *
- * <ol>
- * <li>When the user data is encrypted and the service must authenticate an object that
- * will be used to decrypt it.
- * <li>When the service already acquired the user data but wants to confirm the user's
- * identity before the activity is filled with it.
- * </ol>
- *
- * <p>When this method is called, the Android System displays an UI affordance asking the
- * user to use the fingerprint sensor to auto-fill the activity, and what happens after a
- * successful fingerprint authentication depends on the number of {@link Dataset}s included
- * in this response:
- *
- * <ul>
- * <li>If it's empty (scenario #1 above), the Android System will call {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
- * flags.
- * <li>If it contains one dataset, the activity will be auto-filled right away.
- * <li>If it contains many datasets, the Android System will show dataset picker UI, and
- * then auto-fill the activity once the user select the proper datased.
- * </ul>
- *
- * <p>If the fingerprint authentication fails, the Android System will call {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR}
- * in the flags.
- *
- * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
- * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
- * authentication is available before using this method, and use other alternatives (such as
- * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
- * called when fingerprint is not available, Android System will call {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} passing {@link
- * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
- * in the flags, but it would be wasting system resources (and worsening the user
- * experience) in the process.
- *
- * @param crypto object that will be authenticated.
- * @param extras when set, will be passed back in the {@link
- * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
- * int)} call so it could be used by the service to handle state.
- * @param flags optional parameters, currently ignored.
+ * @param id A required id to identify this dataset for future interactions related to it.
*/
- public Builder requiresFingerprintAuthentication(CryptoObject crypto,
- @Nullable Bundle extras, int flags) {
- // TODO(b/33197203): should we allow crypto to be null?
- Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
- return requiresAuthentication(crypto, extras, flags);
+ public Builder(@NonNull String id) {
+ mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
}
- private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
- int flags) {
- // There can be only one!
- Preconditions.checkState(!mRequiresAuth,
- "requires-authentication methods already called");
- // TODO(b/33197203): make sure that either this method or setExtras() is called, but
- // not both
- mExtras = extras;
- mFlags = flags;
- mRequiresAuth = true;
- if (cryptoObject != null) {
- mHasCryptoObject = true;
- mCryptoOpId = cryptoObject.getOpId();
- }
+ /**
+ * Requires a fill response authentication before auto-filling the activity with
+ * any dataset in this response. This is typically useful when a user interaction
+ * is required to unlock their data vault if you encrypt the dataset labels and
+ * dataset data. It is recommended to encrypt only the sensitive data and not the
+ * dataset labels which would allow auth on the dataset level leading to a better
+ * user experience. Note that if you use sensitive data as a label, for example an
+ * email address, then it should also be encrypted.
+ *
+ * <p>This method is called when you need to provide an authentication
+ * UI for the fill response. For example, when the user's data is stored
+ * encrypted and needs a user interaction to decrypt before offering fill
+ * suggestions.</p>
+ *
+ * <p>When a user initiates an auto fill, the system triggers the provided
+ * intent whose extras will have the {@link android.content.Intent
+ * #EXTRA_AUTO_FILL_ITEM_ID id} of the {@link android.view.autofill.FillResponse})
+ * to authenticate, the {@link android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras}
+ * associated with this response, and a {@link android.content.Intent
+ * #EXTRA_AUTO_FILL_CALLBACK callback} to dispatch the authentication result.</p>
+ *
+ * <p>Once you complete your authentication flow you should use the provided callback
+ * to notify for a failure or a success. In case of a success you need to provide
+ * the fully populated response that is being authenticated. For example, if you
+ * provided an empty {@link FillResponse} because the user's data was locked and
+ * marked that the response needs an authentication then in the response returned
+ * if authentication succeeds you need to provide all available datasets some of
+ * which may need to be further authenticated, for example a credit card whose
+ * CVV needs to be entered.</p>
+ *
+ * <p>The indent sender mechanism allows you to have your authentication UI
+ * implemented as an activity or a service or a receiver. However, the recommended
+ * way is to do this is with an activity which the system will start in the
+ * filled activity's task meaning it will properly work with back, recent apps, and
+ * free-form multi-window, while avoiding the need for the "draw on top of other"
+ * apps special permission. You can still theme your authentication activity's
+ * UI to look like a dialog if desired.</p>
+ *
+ * <p></><strong>Note:</strong> Do not make the provided intent sender
+ * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+ * platform needs to fill in the authentication arguments.</p>
+ *
+ * @param authentication Intent to trigger your authentication flow.
+ *
+ * @see android.app.PendingIntent#getIntentSender()
+ */
+ public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
+ throwIfDestroyed();
+ mAuthentication = authentication;
return this;
}
/**
- * Adds a new {@link Dataset} to this response.
+ * Adds a new {@link Dataset} to this response. Adding a dataset with the
+ * same id updates the existing one.
*
* @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
- mDatasets.add(dataset);
- for (DatasetField field : dataset.getFields()) {
- mSavableIds.add(field.getId());
+ public@NonNull Builder addDataset(@Nullable Dataset dataset) {
+ throwIfDestroyed();
+ if (dataset == null) {
+ return this;
+ }
+ if (mDatasets == null) {
+ mDatasets = new ArraySet<>();
+ }
+ final int datasetCount = mDatasets.size();
+ for (int i = 0; i < datasetCount; i++) {
+ if (mDatasets.valueAt(i).getName().equals(dataset.getName())) {
+ throw new IllegalArgumentException("Duplicate dataset name: "
+ + dataset.getName());
+ }
+ }
+ if (!mDatasets.add(dataset)) {
+ return this;
+ }
+ final int fieldCount = dataset.getFieldIds().size();
+ for (int i = 0; i < fieldCount; i++) {
+ final AutoFillId id = dataset.getFieldIds().get(i);
+ if (mSavableIds == null) {
+ mSavableIds = new ArraySet<>();
+ }
+ mSavableIds.add(id);
}
return this;
}
@@ -374,27 +319,35 @@
*
* <p>See {@link FillResponse} for examples.
*/
- public Builder addSavableFields(AutoFillId... ids) {
+ public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
+ throwIfDestroyed();
+ if (ids == null) {
+ return this;
+ }
for (AutoFillId id : ids) {
+ if (mSavableIds == null) {
+ mSavableIds = new ArraySet<>();
+ }
mSavableIds.add(id);
}
return this;
}
/**
- * Sets a {@link Bundle} that will be passed to subsequent calls to {@link
- * android.service.autofill.AutoFillService} methods such as {@link
+ * Sets a {@link Bundle} that will be passed to subsequent APIs that
+ * manipulate this response. For example, they are passed in as {@link
+ * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
+ * authentication flow and to subsequent calls to {@link
+ * android.service.autofill.AutoFillService#onFillRequest(
+ * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
+ * android.service.autofill.FillCallback)} and {@link
* android.service.autofill.AutoFillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)},
- * using {@link
- * android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
- *
- * <p>It can be used when to keep service state in between calls.
+ * android.app.assist.AssistStructure, Bundle,
+ * android.service.autofill.SaveCallback)}.
*/
public Builder setExtras(Bundle extras) {
- // TODO(b/33197203): make sure that either this method or the requires-Authentication
- // ones are called, but not both
- mExtras = Objects.requireNonNull(extras, "extras cannot be null");
+ throwIfDestroyed();
+ mExtras = extras;
return this;
}
@@ -402,8 +355,16 @@
* Builds a new {@link FillResponse} instance.
*/
public FillResponse build() {
+ throwIfDestroyed();
+ mDestroyed = true;
return new FillResponse(this);
}
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Already called #build()");
+ }
+ }
}
/////////////////////////////////////
@@ -412,14 +373,12 @@
@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)
- .append(", flags=").append(mFlags)
- .append(", requiresAuth: ").append(mRequiresAuth)
- .append(", hasCrypto: ").append(mHasCryptoObject);
+ final StringBuilder builder = new StringBuilder(
+ "FillResponse: [id=").append(mId)
+ .append(", datasets=").append(mDatasets)
+ .append(", savableIds=").append(mSavableIds)
+ .append(", hasExtras=").append(mExtras != null)
+ .append(", hasAuthentication=").append(mAuthentication != null);
return builder.append(']').toString();
}
@@ -434,33 +393,34 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeList(mDatasets);
- parcel.writeParcelableArray(mSavableIds, 0);
- parcel.writeBundle(mExtras);
- parcel.writeInt(mFlags);
- parcel.writeInt(mRequiresAuth ? 1 : 0);
- parcel.writeInt(mHasCryptoObject ? 1 : 0);
- if (mHasCryptoObject) {
- parcel.writeLong(mCryptoOpId);
- }
- }
-
- private FillResponse(Parcel parcel) {
- mDatasets = new ArrayList<>();
- parcel.readList(mDatasets, null);
- mSavableIds = parcel.readParcelableArray(null, AutoFillId.class);
- mExtras = parcel.readBundle();
- mFlags = parcel.readInt();
- mRequiresAuth = parcel.readInt() == 1;
- mHasCryptoObject = parcel.readInt() == 1;
- mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
+ parcel.writeString(mId);
+ parcel.writeTypedArraySet(mDatasets, 0);
+ parcel.writeTypedArraySet(mSavableIds, 0);
+ parcel.writeParcelable(mExtras, 0);
+ parcel.writeParcelable(mAuthentication, 0);
}
public static final Parcelable.Creator<FillResponse> CREATOR =
new Parcelable.Creator<FillResponse>() {
@Override
- public FillResponse createFromParcel(Parcel source) {
- return new FillResponse(source);
+ public FillResponse createFromParcel(Parcel parcel) {
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final Builder builder = new Builder(parcel.readString());
+ final ArraySet<Dataset> datasets = parcel.readTypedArraySet(null);
+ final int datasetCount = (datasets != null) ? datasets.size() : 0;
+ for (int i = 0; i < datasetCount; i++) {
+ builder.addDataset(datasets.valueAt(i));
+ }
+ final ArraySet<AutoFillId> fillIds = parcel.readTypedArraySet(null);
+ final int fillIdCount = (fillIds != null) ? fillIds.size() : 0;
+ for (int i = 0; i < fillIdCount; i++) {
+ builder.addSavableFields(fillIds.valueAt(i));
+ }
+ builder.setExtras(parcel.readParcelable(null));
+ builder.setAuthentication(parcel.readParcelable(null));
+ return builder.build();
}
@Override
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 9a39a17..51587a7 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -250,7 +250,7 @@
mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button);
mDefaultActivityButton.setOnClickListener(mCallbacks);
mDefaultActivityButton.setOnLongClickListener(mCallbacks);
- mDefaultActivityButtonImage = mDefaultActivityButton.findViewById(R.id.image);
+ mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button);
expandButton.setOnClickListener(mCallbacks);
@@ -282,7 +282,7 @@
mExpandActivityOverflowButton = expandButton;
mExpandActivityOverflowButtonImage =
- expandButton.findViewById(R.id.image);
+ (ImageView) expandButton.findViewById(R.id.image);
mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
mAdapter = new ActivityChooserViewAdapter();
@@ -760,7 +760,7 @@
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.activity_chooser_view_list_item, parent, false);
convertView.setId(ITEM_VIEW_TYPE_FOOTER);
- TextView titleView = convertView.findViewById(R.id.title);
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
titleView.setText(mContext.getString(
R.string.activity_chooser_view_see_all));
}
@@ -772,11 +772,11 @@
}
PackageManager packageManager = mContext.getPackageManager();
// Set the icon
- ImageView iconView = convertView.findViewById(R.id.icon);
+ ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
ResolveInfo activity = (ResolveInfo) getItem(position);
iconView.setImageDrawable(activity.loadIcon(packageManager));
// Set the title.
- TextView titleView = convertView.findViewById(R.id.title);
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
titleView.setText(activity.loadLabel(packageManager));
// Highlight the default.
if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 06d4868..68e6809 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -451,7 +451,7 @@
private View getPermissionsView(int which, boolean showRevokeUI) {
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
- LinearLayout displayList = permsView.findViewById(R.id.perms_list);
+ LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions);
displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
@@ -517,8 +517,8 @@
CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
View permView = inflater.inflate(R.layout.app_permission_item_old, null);
- TextView permGrpView = permView.findViewById(R.id.permission_group);
- TextView permDescView = permView.findViewById(R.id.permission_list);
+ TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group);
+ TextView permDescView = (TextView) permView.findViewById(R.id.permission_list);
ImageView imgView = (ImageView)permView.findViewById(R.id.perm_icon);
imgView.setImageDrawable(icon);
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 81f0d3d..bbc50da 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -388,7 +388,7 @@
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
- text = view.findViewById(mFieldId);
+ text = (TextView) view.findViewById(mFieldId);
if (text == null) {
throw new RuntimeException("Failed to find view with ID "
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 1b899db..557d411 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -316,9 +316,9 @@
View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
mDelegator.addView(content);
- mListView = mDelegator.findViewById(R.id.list);
- mDayNamesHeader = content.findViewById(R.id.day_names);
- mMonthName = content.findViewById(R.id.month_name);
+ mListView = (ListView) mDelegator.findViewById(R.id.list);
+ mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
+ mMonthName = (TextView) content.findViewById(R.id.month_name);
setUpHeader();
setUpListView();
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 907250a..f712685 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -115,10 +115,10 @@
mDelegator.addView(mContainer);
// Set up header views.
- final ViewGroup header = mContainer.findViewById(R.id.date_picker_header);
- mHeaderYear = header.findViewById(R.id.date_picker_header_year);
+ final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
+ mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
mHeaderYear.setOnClickListener(mOnHeaderClickListener);
- mHeaderMonthDay = header.findViewById(R.id.date_picker_header_date);
+ mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
// For the sake of backwards compatibility, attempt to extract the text
@@ -154,10 +154,10 @@
a.recycle();
// Set up picker container.
- mAnimator = mContainer.findViewById(R.id.animator);
+ mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
// Set up day picker view.
- mDayPickerView = mAnimator.findViewById(R.id.date_picker_day_picker);
+ mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
@@ -165,7 +165,7 @@
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
// Set up year picker view.
- mYearPickerView = mAnimator.findViewById(R.id.date_picker_year_picker);
+ mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 63621e1..8d5bf8f 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -225,7 +225,7 @@
public Object instantiateItem(ViewGroup container, int position) {
final View itemView = mInflater.inflate(mLayoutResId, container, false);
- final SimpleMonthView v = itemView.findViewById(mCalendarViewId);
+ final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId);
v.setOnDayClickListener(mOnDayClickListener);
v.setMonthTextAppearance(mMonthTextAppearance);
v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
index 058baa6..94022ae 100644
--- a/core/java/android/widget/DayPickerViewPager.java
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -137,10 +137,9 @@
}
@Override
- protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
- View childToSkip) {
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
if (predicate.apply(this)) {
- return (T) this;
+ return this;
}
// Always try the selected view first.
@@ -149,7 +148,7 @@
if (current != childToSkip && current != null) {
final View v = current.findViewByPredicate(predicate);
if (v != null) {
- return (T) v;
+ return v;
}
}
@@ -161,7 +160,7 @@
final View v = child.findViewByPredicate(predicate);
if (v != null) {
- return (T) v;
+ return v;
}
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index b7da04e..80780a6 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3740,21 +3740,20 @@
* @removed For internal use only. This should have been hidden.
*/
@Override
- protected <T extends View> T findViewTraversal(@IdRes int id) {
- // First look in our children, then in any header and footer views that
- // may be scrolled off.
- View v = super.findViewTraversal(id);
+ protected View findViewTraversal(@IdRes int id) {
+ View v;
+ v = super.findViewTraversal(id);
if (v == null) {
v = findViewInHeadersOrFooters(mHeaderViewInfos, id);
if (v != null) {
- return (T) v;
+ return v;
}
v = findViewInHeadersOrFooters(mFooterViewInfos, id);
if (v != null) {
- return (T) v;
+ return v;
}
}
- return (T) v;
+ return v;
}
View findViewInHeadersOrFooters(ArrayList<FixedViewInfo> where, int id) {
@@ -3783,22 +3782,21 @@
* @removed For internal use only. This should have been hidden.
*/
@Override
- protected <T extends View> T findViewWithTagTraversal(Object tag) {
- // First look in our children, then in any header and footer views that
- // may be scrolled off.
- View v = super.findViewWithTagTraversal(tag);
+ protected View findViewWithTagTraversal(Object tag) {
+ View v;
+ v = super.findViewWithTagTraversal(tag);
if (v == null) {
v = findViewWithTagInHeadersOrFooters(mHeaderViewInfos, tag);
if (v != null) {
- return (T) v;
+ return v;
}
v = findViewWithTagInHeadersOrFooters(mFooterViewInfos, tag);
if (v != null) {
- return (T) v;
+ return v;
}
}
- return (T) v;
+ return v;
}
View findViewWithTagInHeadersOrFooters(ArrayList<FixedViewInfo> where, Object tag) {
@@ -3831,21 +3829,21 @@
* @hide
*/
@Override
- protected <T extends View> T findViewByPredicateTraversal(
- Predicate<View> predicate, View childToSkip) {
- View v = super.findViewByPredicateTraversal(predicate, childToSkip);
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ View v;
+ v = super.findViewByPredicateTraversal(predicate, childToSkip);
if (v == null) {
v = findViewByPredicateInHeadersOrFooters(mHeaderViewInfos, predicate, childToSkip);
if (v != null) {
- return (T) v;
+ return v;
}
v = findViewByPredicateInHeadersOrFooters(mFooterViewInfos, predicate, childToSkip);
if (v != null) {
- return (T) v;
+ return v;
}
}
- return (T) v;
+ return v;
}
/**
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 8e04f1c..8008637 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -257,13 +257,13 @@
.getText(com.android.internal.R.string.lockscreen_transport_play_description);
mPauseDescription = res
.getText(com.android.internal.R.string.lockscreen_transport_pause_description);
- mPauseButton = v.findViewById(com.android.internal.R.id.pause);
+ mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause);
if (mPauseButton != null) {
mPauseButton.requestFocus();
mPauseButton.setOnClickListener(mPauseListener);
}
- mFfwdButton = v.findViewById(com.android.internal.R.id.ffwd);
+ mFfwdButton = (ImageButton) v.findViewById(com.android.internal.R.id.ffwd);
if (mFfwdButton != null) {
mFfwdButton.setOnClickListener(mFfwdListener);
if (!mFromXml) {
@@ -271,7 +271,7 @@
}
}
- mRewButton = v.findViewById(com.android.internal.R.id.rew);
+ mRewButton = (ImageButton) v.findViewById(com.android.internal.R.id.rew);
if (mRewButton != null) {
mRewButton.setOnClickListener(mRewListener);
if (!mFromXml) {
@@ -280,16 +280,16 @@
}
// By default these are hidden. They will be enabled when setPrevNextListeners() is called
- mNextButton = v.findViewById(com.android.internal.R.id.next);
+ mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next);
if (mNextButton != null && !mFromXml && !mListenersSet) {
mNextButton.setVisibility(View.GONE);
}
- mPrevButton = v.findViewById(com.android.internal.R.id.prev);
+ mPrevButton = (ImageButton) v.findViewById(com.android.internal.R.id.prev);
if (mPrevButton != null && !mFromXml && !mListenersSet) {
mPrevButton.setVisibility(View.GONE);
}
- mProgress = v.findViewById(com.android.internal.R.id.mediacontroller_progress);
+ mProgress = (ProgressBar) v.findViewById(com.android.internal.R.id.mediacontroller_progress);
if (mProgress != null) {
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
@@ -298,8 +298,8 @@
mProgress.setMax(1000);
}
- mEndTime = v.findViewById(com.android.internal.R.id.time);
- mCurrentTime = v.findViewById(com.android.internal.R.id.time_current);
+ mEndTime = (TextView) v.findViewById(com.android.internal.R.id.time);
+ mCurrentTime = (TextView) v.findViewById(com.android.internal.R.id.time_current);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 5199b26..989927e 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -35,8 +35,8 @@
import android.transition.Transition;
import android.transition.Transition.EpicenterCallback;
import android.transition.Transition.TransitionListener;
-import android.transition.Transition.TransitionListenerAdapter;
import android.transition.TransitionInflater;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AttributeSet;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5505f2f..359d04e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1578,7 +1578,7 @@
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final Context context = root.getContext();
- final ViewGroup target = root.findViewById(viewId);
+ final ViewGroup target = (ViewGroup) root.findViewById(viewId);
if (target == null) return;
if (nestedViews != null) {
// Inflate nested views and add as children
@@ -1757,7 +1757,7 @@
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = root.findViewById(viewId);
+ final TextView target = (TextView) root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
if (isRelative) {
@@ -1857,7 +1857,7 @@
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = root.findViewById(viewId);
+ final TextView target = (TextView) root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
}
@@ -2045,7 +2045,7 @@
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = root.findViewById(viewId);
+ final TextView target = (TextView) root.findViewById(viewId);
if (target == null) return;
Drawable[] drawables = isRelative
? target.getCompoundDrawablesRelative()
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index fbb8993..f833d1b 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -286,7 +286,7 @@
v.setTag(new ChildViewCache(v));
// Set up icon.
- final ImageView iconRefine = v.findViewById(R.id.edit_query);
+ final ImageView iconRefine = (ImageView) v.findViewById(R.id.edit_query);
iconRefine.setImageResource(mCommitIconResId);
return v;
@@ -304,11 +304,11 @@
public final ImageView mIconRefine;
public ChildViewCache(View v) {
- mText1 = v.findViewById(com.android.internal.R.id.text1);
- mText2 = v.findViewById(com.android.internal.R.id.text2);
- mIcon1 = v.findViewById(com.android.internal.R.id.icon1);
- mIcon2 = v.findViewById(com.android.internal.R.id.icon2);
- mIconRefine = v.findViewById(com.android.internal.R.id.edit_query);
+ mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1);
+ mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2);
+ mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1);
+ mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2);
+ mIconRefine = (ImageView) v.findViewById(com.android.internal.R.id.edit_query);
}
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 7e2cadf..32418cd 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -619,7 +619,7 @@
mTabWidget, // tab widget is the parent
false); // no inflate params
- final TextView tv = tabIndicator.findViewById(R.id.title);
+ final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
tv.setText(mLabel);
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
@@ -653,8 +653,8 @@
mTabWidget, // tab widget is the parent
false); // no inflate params
- final TextView tv = tabIndicator.findViewById(R.id.title);
- final ImageView iconView = tabIndicator.findViewById(R.id.icon);
+ final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
+ final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
// when icon is gone by default, we're in exclusive mode
final boolean exclusive = iconView.getVisibility() == View.GONE;
diff --git a/core/java/android/widget/TextInputTimePickerView.java b/core/java/android/widget/TextInputTimePickerView.java
new file mode 100644
index 0000000..ef91576
--- /dev/null
+++ b/core/java/android/widget/TextInputTimePickerView.java
@@ -0,0 +1,249 @@
+/*
+ * 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 android.content.Context;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * View to show text input based time picker with hour and minute fields and an optional AM/PM
+ * spinner.
+ *
+ * @hide
+ */
+public class TextInputTimePickerView extends RelativeLayout {
+ public static final int HOURS = 0;
+ public static final int MINUTES = 1;
+ public static final int AMPM = 2;
+
+ private static final int AM = 0;
+ private static final int PM = 1;
+
+ private final EditText mHourEditText;
+ private final EditText mMinuteEditText;
+ private final TextView mInputSeparatorView;
+ private final Spinner mAmPmSpinner;
+ private final TextView mErrorLabel;
+ private final TextView mHourLabel;
+ private final TextView mMinuteLabel;
+
+ private boolean mIs24Hour;
+ private boolean mHourFormatStartsAtZero;
+ private OnValueTypedListener mListener;
+
+ private boolean mErrorShowing;
+
+ interface OnValueTypedListener {
+ void onValueChanged(int inputType, int newValue);
+ }
+
+ public TextInputTimePickerView(Context context) {
+ this(context, null);
+ }
+
+ public TextInputTimePickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs, defStyle, 0);
+ }
+
+ public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle,
+ int defStyleRes) {
+ super(context, attrs, defStyle, defStyleRes);
+
+ inflate(context, R.layout.time_picker_text_input_material, this);
+
+ mHourEditText = (EditText) findViewById(R.id.input_hour);
+ mMinuteEditText = (EditText) findViewById(R.id.input_minute);
+ mInputSeparatorView = (TextView) findViewById(R.id.input_separator);
+ mErrorLabel = (TextView) findViewById(R.id.label_error);
+ mHourLabel = (TextView) findViewById(R.id.label_hour);
+ mMinuteLabel = (TextView) findViewById(R.id.label_minute);
+
+ mHourEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ parseAndSetHourInternal(editable.toString());
+ }
+ });
+
+ mMinuteEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ parseAndSetMinuteInternal(editable.toString());
+ }
+ });
+
+ mAmPmSpinner = (Spinner) findViewById(R.id.am_pm_spinner);
+ final String[] amPmStrings = TimePicker.getAmPmStrings(context);
+ ArrayAdapter<CharSequence> adapter =
+ new ArrayAdapter<CharSequence>(context, R.layout.simple_spinner_dropdown_item);
+ adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[0]));
+ adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[1]));
+ mAmPmSpinner.setAdapter(adapter);
+ mAmPmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapterView, View view, int position,
+ long id) {
+ if (position == 0) {
+ mListener.onValueChanged(AMPM, AM);
+ } else {
+ mListener.onValueChanged(AMPM, PM);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapterView) {}
+ });
+ }
+
+ void setListener(OnValueTypedListener listener) {
+ mListener = listener;
+ }
+
+ void setHourFormat(int maxCharLength) {
+ mHourEditText.setFilters(new InputFilter[] {
+ new InputFilter.LengthFilter(maxCharLength)});
+ mMinuteEditText.setFilters(new InputFilter[] {
+ new InputFilter.LengthFilter(maxCharLength)});
+ }
+
+ boolean validateInput() {
+ final boolean inputValid = parseAndSetHourInternal(mHourEditText.getText().toString())
+ && parseAndSetMinuteInternal(mMinuteEditText.getText().toString());
+ setError(!inputValid);
+ return inputValid;
+ }
+
+ void updateSeparator(String separatorText) {
+ mInputSeparatorView.setText(separatorText);
+ }
+
+ private void setError(boolean enabled) {
+ mErrorShowing = enabled;
+
+ mErrorLabel.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+ mHourLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
+ mMinuteLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ /**
+ * Computes the display value and updates the text of the view.
+ * <p>
+ * This method should be called whenever the current value or display
+ * properties (leading zeroes, max digits) change.
+ */
+ void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
+ boolean hourFormatStartsAtZero) {
+ final String format = "%d";
+
+ mIs24Hour = is24Hour;
+ mHourFormatStartsAtZero = hourFormatStartsAtZero;
+
+ mAmPmSpinner.setVisibility(is24Hour ? View.INVISIBLE : View.VISIBLE);
+
+ mHourEditText.setText(String.format(format, localizedHour));
+ mMinuteEditText.setText(String.format(format, minute));
+
+ if (amOrPm == AM) {
+ mAmPmSpinner.setSelection(0);
+ } else {
+ mAmPmSpinner.setSelection(1);
+ }
+
+ if (mErrorShowing) {
+ validateInput();
+ }
+ }
+
+ private boolean parseAndSetHourInternal(String input) {
+ try {
+ final int hour = Integer.parseInt(input);
+ if (!isValidLocalizedHour(hour)) {
+ final int minHour = mHourFormatStartsAtZero ? 0 : 1;
+ final int maxHour = mIs24Hour ? 23 : 11 + minHour;
+ mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(
+ MathUtils.constrain(hour, minHour, maxHour)));
+ return false;
+ }
+ mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(hour));
+ return true;
+ } catch (NumberFormatException e) {
+ // Do nothing since we cannot parse the input.
+ return false;
+ }
+ }
+
+ private boolean parseAndSetMinuteInternal(String input) {
+ try {
+ final int minutes = Integer.parseInt(input);
+ if (minutes < 0 || minutes > 59) {
+ mListener.onValueChanged(MINUTES, MathUtils.constrain(minutes, 0, 59));
+ return false;
+ }
+ mListener.onValueChanged(MINUTES, minutes);
+ return true;
+ } catch (NumberFormatException e) {
+ // Do nothing since we cannot parse the input.
+ return false;
+ }
+ }
+
+ private boolean isValidLocalizedHour(int localizedHour) {
+ final int minHour = mHourFormatStartsAtZero ? 0 : 1;
+ final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
+ return localizedHour >= minHour && localizedHour <= maxHour;
+ }
+
+ private int getHourOfDayFromLocalizedHour(int localizedHour) {
+ int hourOfDay = localizedHour;
+ if (mIs24Hour) {
+ if (!mHourFormatStartsAtZero && localizedHour == 24) {
+ hourOfDay = 0;
+ }
+ } else {
+ if (!mHourFormatStartsAtZero && localizedHour == 12) {
+ hourOfDay = 0;
+ }
+ if (mAmPmSpinner.getSelectedItemPosition() == 1) {
+ hourOfDay += 12;
+ }
+ }
+ return hourOfDay;
+ }
+}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index e6cd798..9f38b04 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -278,6 +278,16 @@
return mDelegate.getBaseline();
}
+ /**
+ * Validates whether current input by the user is a valid time based on the locale. TimePicker
+ * will show an error message to the user if the time is not valid.
+ *
+ * @return {@code true} if the input is valid, {@code false} otherwise
+ */
+ public boolean validateInput() {
+ return mDelegate.validateInput();
+ }
+
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
@@ -341,6 +351,8 @@
void setIs24Hour(boolean is24Hour);
boolean is24Hour();
+ boolean validateInput();
+
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
void setEnabled(boolean enabled);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 1d37a21..3a09063 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -16,12 +16,14 @@
package android.widget;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.icu.text.DecimalFormatSymbols;
import android.os.Parcelable;
import android.text.SpannableStringBuilder;
import android.text.format.DateFormat;
@@ -40,11 +42,14 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.RadialTimePickerView.OnValueSelectedListener;
+import android.widget.TextInputTimePickerView.OnValueTypedListener;
import com.android.internal.R;
import com.android.internal.widget.NumericTextView;
import com.android.internal.widget.NumericTextView.OnValueChangedListener;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Calendar;
/**
@@ -58,6 +63,13 @@
*/
private static final long DELAY_COMMIT_MILLIS = 2000;
+ @IntDef({FROM_EXTERNAL_API, FROM_RADIAL_PICKER, FROM_INPUT_PICKER})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ChangeSource {}
+ private static final int FROM_EXTERNAL_API = 0;
+ private static final int FROM_RADIAL_PICKER = 1;
+ private static final int FROM_INPUT_PICKER = 2;
+
// Index used by RadialPickerLayout
private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
@@ -78,6 +90,15 @@
private final RadialTimePickerView mRadialTimePickerView;
private final TextView mSeparatorView;
+ private boolean mRadialPickerModeEnabled = true;
+ private final ImageButton mRadialTimePickerModeButton;
+ private final String mRadialTimePickerModeEnabledDescription;
+ private final String mTextInputPickerModeEnabledDescription;
+ private final View mRadialTimePickerHeader;
+ private final View mTextInputPickerHeader;
+
+ private final TextInputTimePickerView mTextInputPickerView;
+
private final Calendar mTempCalendar;
// Accessibility strings.
@@ -116,8 +137,8 @@
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
R.layout.time_picker_material);
final View mainView = inflater.inflate(layoutResourceId, delegator);
- final View headerView = mainView.findViewById(R.id.time_header);
- headerView.setOnTouchListener(new NearestTouchDelegate());
+ mRadialTimePickerHeader = mainView.findViewById(R.id.time_header);
+ mRadialTimePickerHeader.setOnTouchListener(new NearestTouchDelegate());
// Set up hour/minute labels.
mHourView = (NumericTextView) mainView.findViewById(R.id.hours);
@@ -170,6 +191,8 @@
headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
}
+ mTextInputPickerHeader = mainView.findViewById(R.id.input_header);
+
if (headerTextColor != null) {
mHourView.setTextColor(headerTextColor);
mSeparatorView.setTextColor(headerTextColor);
@@ -180,7 +203,10 @@
// Set up header background, if available.
if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
- headerView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
+ mRadialTimePickerHeader.setBackground(a.getDrawable(
+ R.styleable.TimePicker_headerBackground));
+ mTextInputPickerHeader.setBackground(a.getDrawable(
+ R.styleable.TimePicker_headerBackground));
}
a.recycle();
@@ -189,6 +215,22 @@
mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
mRadialTimePickerView.setOnValueSelectedListener(mOnValueSelectedListener);
+ mTextInputPickerView = (TextInputTimePickerView) mainView.findViewById(R.id.input_mode);
+ mTextInputPickerView.setListener(mOnValueTypedListener);
+
+ mRadialTimePickerModeButton =
+ (ImageButton) mainView.findViewById(R.id.toggle_mode);
+ mRadialTimePickerModeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleRadialPickerMode();
+ }
+ });
+ mRadialTimePickerModeEnabledDescription = context.getResources().getString(
+ R.string.time_picker_radial_mode_description);
+ mTextInputPickerModeEnabledDescription = context.getResources().getString(
+ R.string.time_picker_text_input_mode_description);
+
mAllowAutoAdvance = true;
updateHourFormat();
@@ -200,6 +242,34 @@
initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX);
}
+ private void toggleRadialPickerMode() {
+ if (mRadialPickerModeEnabled) {
+ mRadialTimePickerView.setVisibility(View.GONE);
+ mRadialTimePickerHeader.setVisibility(View.GONE);
+ mTextInputPickerHeader.setVisibility(View.VISIBLE);
+ mTextInputPickerView.setVisibility(View.VISIBLE);
+ mRadialTimePickerModeButton.setImageResource(R.drawable.btn_event_material);
+ mRadialTimePickerModeButton.setContentDescription(
+ mRadialTimePickerModeEnabledDescription);
+ mRadialPickerModeEnabled = false;
+ } else {
+ mRadialTimePickerView.setVisibility(View.VISIBLE);
+ mRadialTimePickerHeader.setVisibility(View.VISIBLE);
+ mTextInputPickerHeader.setVisibility(View.GONE);
+ mTextInputPickerView.setVisibility(View.GONE);
+ mRadialTimePickerModeButton.setImageResource(R.drawable.btn_keyboard_key_material);
+ mRadialTimePickerModeButton.setContentDescription(
+ mTextInputPickerModeEnabledDescription);
+ updateTextInputPicker();
+ mRadialPickerModeEnabled = true;
+ }
+ }
+
+ @Override
+ public boolean validateInput() {
+ return mTextInputPickerView.validateInput();
+ }
+
/**
* Ensures that a TextView is wide enough to contain its text without
* wrapping or clipping. Measures the specified view and sets the minimum
@@ -249,9 +319,16 @@
final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
mHourView.setRange(minHour, maxHour);
mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
+
+ final String[] digits = DecimalFormatSymbols.getInstance(mLocale).getDigitStrings();
+ int maxCharLength = 0;
+ for (int i = 0; i < 10; i++) {
+ maxCharLength = Math.max(maxCharLength, digits[i].length());
+ }
+ mTextInputPickerView.setHourFormat(maxCharLength * 2);
}
- private static final CharSequence obtainVerbatim(String text) {
+ static final CharSequence obtainVerbatim(String text) {
return new SpannableStringBuilder().append(text,
new TtsSpan.VerbatimBuilder(text).build(), 0);
}
@@ -333,10 +410,16 @@
updateHeaderSeparator();
updateHeaderMinute(mCurrentMinute, false);
updateRadialPicker(index);
+ updateTextInputPicker();
mDelegator.invalidate();
}
+ private void updateTextInputPicker() {
+ mTextInputPickerView.updateTextInputValues(getLocalizedHour(mCurrentHour), mCurrentMinute,
+ mCurrentHour < 12 ? AM : PM, mIs24Hour, mHourFormatStartsAtZero);
+ }
+
private void updateRadialPicker(int index) {
mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
setCurrentItemShowing(index, false, true);
@@ -381,10 +464,10 @@
*/
@Override
public void setHour(int hour) {
- setHourInternal(hour, false, true);
+ setHourInternal(hour, FROM_EXTERNAL_API, true);
}
- private void setHourInternal(int hour, boolean isFromPicker, boolean announce) {
+ private void setHourInternal(int hour, @ChangeSource int source, boolean announce) {
if (mCurrentHour == hour) {
return;
}
@@ -393,10 +476,13 @@
updateHeaderHour(hour, announce);
updateHeaderAmPm();
- if (!isFromPicker) {
+ if (source != FROM_RADIAL_PICKER) {
mRadialTimePickerView.setCurrentHour(hour);
mRadialTimePickerView.setAmOrPm(hour < 12 ? AM : PM);
}
+ if (source != FROM_INPUT_PICKER) {
+ updateTextInputPicker();
+ }
mDelegator.invalidate();
onTimeChanged();
@@ -424,10 +510,10 @@
*/
@Override
public void setMinute(int minute) {
- setMinuteInternal(minute, false);
+ setMinuteInternal(minute, FROM_EXTERNAL_API);
}
- private void setMinuteInternal(int minute, boolean isFromPicker) {
+ private void setMinuteInternal(int minute, @ChangeSource int source) {
if (mCurrentMinute == minute) {
return;
}
@@ -435,9 +521,12 @@
mCurrentMinute = minute;
updateHeaderMinute(minute, true);
- if (!isFromPicker) {
+ if (source != FROM_RADIAL_PICKER) {
mRadialTimePickerView.setCurrentMinute(minute);
}
+ if (source != FROM_INPUT_PICKER) {
+ updateTextInputPicker();
+ }
mDelegator.invalidate();
onTimeChanged();
@@ -661,6 +750,7 @@
separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
}
mSeparatorView.setText(separatorText);
+ mTextInputPickerView.updateSeparator(separatorText);
}
static private int lastIndexOfAny(String str, char[] any) {
@@ -712,7 +802,7 @@
if (mRadialTimePickerView.setAmOrPm(amOrPm)) {
mCurrentHour = getHour();
-
+ updateTextInputPicker();
if (mOnTimeChangedListener != null) {
mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
}
@@ -726,7 +816,7 @@
switch (pickerType) {
case RadialTimePickerView.HOURS:
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
- setHourInternal(newValue, true, !isTransition);
+ setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition);
if (isTransition) {
setCurrentItemShowing(MINUTE_INDEX, true, false);
@@ -735,7 +825,7 @@
}
break;
case RadialTimePickerView.MINUTES:
- setMinuteInternal(newValue, true);
+ setMinuteInternal(newValue, FROM_RADIAL_PICKER);
break;
}
@@ -745,6 +835,23 @@
}
};
+ private final OnValueTypedListener mOnValueTypedListener = new OnValueTypedListener() {
+ @Override
+ public void onValueChanged(int pickerType, int newValue) {
+ switch (pickerType) {
+ case TextInputTimePickerView.HOURS:
+ setHourInternal(newValue, FROM_INPUT_PICKER, false);
+ break;
+ case TextInputTimePickerView.MINUTES:
+ setMinuteInternal(newValue, FROM_INPUT_PICKER);
+ break;
+ case TextInputTimePickerView.AMPM:
+ setAmOrPm(newValue);
+ break;
+ }
+ }
+ };
+
/** Listener for keyboard interaction. */
private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
@Override
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 4634631..7ef54a5 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -86,7 +86,7 @@
inflater.inflate(layoutResourceId, mDelegator, true);
// hour
- mHourSpinner = delegator.findViewById(R.id.hour);
+ mHourSpinner = (NumberPicker) delegator.findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
updateInputState();
@@ -100,17 +100,17 @@
onTimeChanged();
}
});
- mHourSpinnerInput = mHourSpinner.findViewById(R.id.numberpicker_input);
+ mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input);
mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// divider (only for the new widget style)
- mDivider = mDelegator.findViewById(R.id.divider);
+ mDivider = (TextView) mDelegator.findViewById(R.id.divider);
if (mDivider != null) {
setDividerText();
}
// minute
- mMinuteSpinner = mDelegator.findViewById(R.id.minute);
+ mMinuteSpinner = (NumberPicker) mDelegator.findViewById(R.id.minute);
mMinuteSpinner.setMinValue(0);
mMinuteSpinner.setMaxValue(59);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
@@ -138,7 +138,7 @@
onTimeChanged();
}
});
- mMinuteSpinnerInput = mMinuteSpinner.findViewById(R.id.numberpicker_input);
+ mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input);
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// Get the localized am/pm strings and use them in the spinner.
@@ -173,13 +173,13 @@
onTimeChanged();
}
});
- mAmPmSpinnerInput = mAmPmSpinner.findViewById(R.id.numberpicker_input);
+ mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
}
if (isAmPmAtStart()) {
// Move the am/pm view to the beginning
- ViewGroup amPmParent = delegator.findViewById(R.id.timePickerLayout);
+ ViewGroup amPmParent = (ViewGroup) delegator.findViewById(R.id.timePickerLayout);
amPmParent.removeView(amPmView);
amPmParent.addView(amPmView, 0);
// Swap layout margins if needed. They may be not symmetrical (Old Standard Theme
@@ -219,6 +219,11 @@
}
}
+ @Override
+ public boolean validateInput() {
+ return true;
+ }
+
private void getHourFormatData() {
final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
(mIs24HourView) ? "Hm" : "hm");
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index bf0601d..789e60b 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -299,7 +299,7 @@
if (mNextView == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
- TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
+ TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
if (tv == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index 1a3ca86..69b79971 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -264,7 +264,7 @@
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(com.android.internal.R.layout.zoom_container, container);
- mControls = container.findViewById(com.android.internal.R.id.zoomControls);
+ mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls);
mControls.setOnZoomInClickListener(new OnClickListener() {
public void onClick(View v) {
dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 20f10b3..9cbbc5a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -53,9 +53,6 @@
void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
void setWindowState(int window, int state);
- void buzzBeepBlinked();
- void notificationLightOff();
- void notificationLightPulse(int argb, int millisOn, int millisOff);
void showRecentApps(boolean triggeredFromAltTab, boolean fromHome);
void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
diff --git a/core/java/com/android/internal/widget/DrawingSpace.java b/core/java/com/android/internal/widget/DrawingSpace.java
deleted file mode 100644
index b8222db..0000000
--- a/core/java/com/android/internal/widget/DrawingSpace.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Implementation of {@link android.widget.Space} that uses normal View drawing
- * rather than a no-op. Useful for dialogs and other places where the base View
- * class is too greedy when measured with AT_MOST.
- */
-public final class DrawingSpace extends View {
- public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public DrawingSpace(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DrawingSpace(Context context) {
- this(context, null);
- }
-
- /**
- * Compare to: {@link View#getDefaultSize(int, int)}
- * <p>
- * If mode is AT_MOST, return the child size instead of the parent size
- * (unless it is too big).
- */
- private static int getDefaultSizeNonGreedy(int size, int measureSpec) {
- int result = size;
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
-
- switch (specMode) {
- case MeasureSpec.UNSPECIFIED:
- result = size;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.min(size, specSize);
- break;
- case MeasureSpec.EXACTLY:
- result = specSize;
- break;
- }
- return result;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(
- getDefaultSizeNonGreedy(getSuggestedMinimumWidth(), widthMeasureSpec),
- getDefaultSizeNonGreedy(getSuggestedMinimumHeight(), heightMeasureSpec));
- }
-}
diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java
index 53fa7ab..4fd19c3 100644
--- a/core/java/com/android/internal/widget/WatchHeaderListView.java
+++ b/core/java/com/android/internal/widget/WatchHeaderListView.java
@@ -92,14 +92,13 @@
}
@Override
- protected <T extends View> T findViewByPredicateTraversal(
- Predicate<View> predicate, View childToSkip) {
+ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
View v = super.findViewByPredicateTraversal(predicate, childToSkip);
if (v == null && mTopPanel != null && mTopPanel != childToSkip
&& !mTopPanel.isRootNamespace()) {
- return (T) mTopPanel.findViewByPredicate(predicate);
+ return mTopPanel.findViewByPredicate(predicate);
}
- return (T) v;
+ return v;
}
@Override
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index 2fe5d39..3f7bab2 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -147,6 +147,9 @@
// Needed for kernel to restart syscalls
AllowSyscall(f, 128); // __NR_restart_syscall
+ // b/35034743
+ AllowSyscall(f, 267); // __NR_fstatfs64
+
Trap(f);
if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5c65241..e2fc444 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -247,24 +247,42 @@
static void DropCapabilitiesBoundingSet(JNIEnv* env) {
for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- // Keep CAP_SYS_PTRACE in our bounding set so crash_dump can gain it.
- if (i == CAP_SYS_PTRACE) {
- continue;
- }
-
int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
if (rc == -1) {
if (errno == EINVAL) {
ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
"your kernel is compiled with file capabilities support");
} else {
+ ALOGE("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed");
}
}
}
}
-static void SetCapabilities(JNIEnv* env, int64_t permitted, int64_t effective) {
+static void SetInheritable(JNIEnv* env, uint64_t inheritable) {
+ __user_cap_header_struct capheader;
+ memset(&capheader, 0, sizeof(capheader));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ __user_cap_data_struct capdata[2];
+ if (capget(&capheader, &capdata[0]) == -1) {
+ ALOGE("capget failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "capget failed");
+ }
+
+ capdata[0].inheritable = inheritable;
+ capdata[1].inheritable = inheritable >> 32;
+
+ if (capset(&capheader, &capdata[0]) == -1) {
+ ALOGE("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
+ RuntimeAbort(env, __LINE__, "capset failed");
+ }
+}
+
+static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective,
+ uint64_t inheritable) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -276,9 +294,12 @@
capdata[1].effective = effective >> 32;
capdata[0].permitted = permitted;
capdata[1].permitted = permitted >> 32;
+ capdata[0].inheritable = inheritable;
+ capdata[1].inheritable = inheritable >> 32;
if (capset(&capheader, &capdata[0]) == -1) {
- ALOGE("capset(%" PRId64 ", %" PRId64 ") failed", permitted, effective);
+ ALOGE("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") failed: %s", permitted,
+ effective, inheritable, strerror(errno));
RuntimeAbort(env, __LINE__, "capset failed");
}
}
@@ -532,6 +553,7 @@
EnableKeepCapabilities(env);
}
+ SetInheritable(env, permittedCapabilities);
DropCapabilitiesBoundingSet(env);
bool use_native_bridge = !is_system_server && (instructionSet != NULL)
@@ -604,7 +626,7 @@
}
}
- SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
+ SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);
SetSchedulerPolicy(env);
diff --git a/core/res/res/drawable/btn_event_material.xml b/core/res/res/drawable/btn_event_material.xml
new file mode 100644
index 0000000..47c49cf
--- /dev/null
+++ b/core/res/res/drawable/btn_event_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#90ffffff"
+ android:pathData="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99 .9 -1.99 2L3 19c0 1.1 .89 2 2
+2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z" />
+ <path android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/btn_keyboard_key_material.xml b/core/res/res/drawable/btn_keyboard_key_material.xml
new file mode 100644
index 0000000..14a9492f
--- /dev/null
+++ b/core/res/res/drawable/btn_keyboard_key_material.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#90ffffff"
+ android:pathData="M20 5H4c-1.1 0-1.99 .9 -1.99 2L2 17c0 1.1 .9 2 2 2h16c1.1 0 2-.9
+2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0
+3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9
+7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z" />
+ <path
+ android:pathData="M0 0h24v24H0zm0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 70833d6..8b95f9f 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -15,28 +15,17 @@
limitations under the License.
-->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layoutDirection="ltr">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <!-- Provides a background for the time layout that extends into the button bar area. -->
- <com.android.internal.widget.DrawingSpace
+ <LinearLayout
android:id="@+id/time_header"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_column="@dimen/time_picker_column_start_material"
- android:layout_row="0"
- android:layout_rowSpan="3"
- android:layout_gravity="center|fill"
- android:layoutDirection="locale" />
-
- <RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_column="@dimen/time_picker_column_start_material"
- android:layout_row="1"
- android:layout_gravity="center|fill"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding">
@@ -44,10 +33,8 @@
android:id="@+id/time_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_centerInParent="true"
android:paddingTop="@dimen/timepicker_radial_picker_top_margin"
- android:layout_marginBottom="-12dp">
+ android:orientation="horizontal">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -125,38 +112,71 @@
android:includeFontPadding="false"
android:button="@null" />
</RadioGroup>
- </RelativeLayout>
+ </LinearLayout>
- <ViewStub
- android:id="@id/topPanel"
- android:layout="@layout/alert_dialog_title_material"
+ <TextView
+ android:visibility="gone"
+ android:id="@+id/input_header"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:paddingStart="@dimen/dialog_padding_material"
+ android:paddingEnd="@dimen/dialog_padding_material"
+ android:paddingTop="20dp"
+ android:paddingBottom="20dp"
+ android:includeFontPadding="false"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.InputHeader"
+ android:text="@string/time_picker_header_text"/>
+
+ <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_column="@dimen/time_picker_column_end_material"
- android:layout_row="0"
- android:layout_gravity="top|fill_horizontal"
- android:layoutDirection="locale" />
+ android:orientation="vertical">
- <android.widget.RadialTimePickerView
- android:id="@+id/radial_picker"
- android:layout_width="@dimen/timepicker_radial_picker_dimen"
- android:layout_height="@dimen/timepicker_radial_picker_dimen"
- android:layout_column="@dimen/time_picker_column_end_material"
- android:layout_row="1"
- android:layout_rowWeight="1"
- android:layout_gravity="center|fill"
- android:layout_marginTop="@dimen/timepicker_radial_picker_top_margin"
- android:layout_marginStart="@dimen/timepicker_radial_picker_horizontal_margin"
- android:layout_marginEnd="@dimen/timepicker_radial_picker_horizontal_margin"
- android:layoutDirection="locale" />
+ <android.widget.RadialTimePickerView
+ android:id="@+id/radial_picker"
+ android:layout_width="@dimen/timepicker_radial_picker_dimen"
+ android:layout_height="@dimen/timepicker_radial_picker_dimen"
+ android:layout_gravity="center|fill"
+ android:layout_marginTop="@dimen/timepicker_radial_picker_top_margin"
+ android:layout_marginStart="@dimen/timepicker_radial_picker_horizontal_margin"
+ android:layout_marginEnd="@dimen/timepicker_radial_picker_horizontal_margin"
+ android:layoutDirection="locale" />
- <ViewStub
- android:id="@id/buttonPanel"
- android:layout="@layout/alert_dialog_button_bar_material"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_column="@dimen/time_picker_column_end_material"
- android:layout_row="2"
- android:layout_gravity="bottom|fill_horizontal"
- android:layoutDirection="locale" />
-</GridLayout>
+ <android.widget.TextInputTimePickerView
+ android:id="@+id/input_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ImageButton
+ android:id="@+id/toggle_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:padding="12dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:tint="?attr/colorControlNormal"
+ android:src="@drawable/btn_keyboard_key_material"
+ android:contentDescription="@string/time_picker_text_input_mode_description" />
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ <ViewStub
+ android:id="@id/buttonPanel"
+ android:layout="@layout/alert_dialog_button_bar_material"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale" />
+ </LinearLayout>
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/time_picker_material.xml b/core/res/res/layout/time_picker_material.xml
index 37a7384..7597379 100644
--- a/core/res/res/layout/time_picker_material.xml
+++ b/core/res/res/layout/time_picker_material.xml
@@ -34,4 +34,53 @@
android:layout_marginTop="@dimen/timepicker_radial_picker_top_margin"
android:layout_marginStart="@dimen/timepicker_radial_picker_horizontal_margin"
android:layout_marginEnd="@dimen/timepicker_radial_picker_horizontal_margin" />
+ <TextView
+ android:visibility="gone"
+ android:id="@+id/input_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/dialog_padding_material"
+ android:paddingEnd="@dimen/dialog_padding_material"
+ android:paddingTop="20dp"
+ android:paddingBottom="20dp"
+ android:includeFontPadding="false"
+ android:fontFamily="sans-serif-medium"
+ android:textSize="34sp"
+ android:textColor="@color/white"
+ android:text="@string/time_picker_header_text"/>
+ <android.widget.TextInputTimePickerView
+ android:id="@+id/input_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <ImageButton
+ android:id="@+id/toggle_mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:padding="12dp"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:tint="?attr/colorControlNormal"
+ android:src="@drawable/btn_keyboard_key_material"
+ android:contentDescription="@string/time_picker_text_input_mode_description" />
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ <ViewStub
+ android:id="@id/buttonPanel"
+ android:layout="@layout/alert_dialog_button_bar_material"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale" />
+ </LinearLayout>
+
</LinearLayout>
diff --git a/core/res/res/layout/time_picker_text_input_material.xml b/core/res/res/layout/time_picker_text_input_material.xml
new file mode 100644
index 0000000..f17b80e
--- /dev/null
+++ b/core/res/res/layout/time_picker_text_input_material.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <TextView
+ android:id="@+id/top_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="26dp"
+ android:layout_marginBottom="8dp"
+ android:text="@string/time_picker_prompt_label"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.PromptLabel" />
+
+ <RelativeLayout
+ android:id="@+id/input_block"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/top_label"
+ android:layoutDirection="ltr">
+ <EditText
+ android:id="@+id/input_hour"
+ android:layout_width="50dp"
+ android:layout_height="wrap_content"
+ android:inputType="number"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField" />
+ <TextView
+ android:id="@+id/label_hour"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/input_hour"
+ android:layout_alignStart="@id/input_hour"
+ android:text="@string/time_picker_hour_label"/>
+
+ <TextView
+ android:id="@+id/input_separator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignEnd="@id/input_hour"
+ android:layout_alignBaseline="@id/input_hour"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField" />
+
+ <EditText
+ android:id="@+id/input_minute"
+ android:layout_width="50dp"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@id/input_hour"
+ android:layout_toEndOf="@id/input_separator"
+ android:inputType="number"
+ android:textAppearance="@style/TextAppearance.Material.TimePicker.InputField" />
+ <TextView
+ android:id="@+id/label_minute"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/input_minute"
+ android:layout_alignStart="@id/input_minute"
+ android:text="@string/time_picker_minute_label"/>
+
+ <TextView
+ android:visibility="invisible"
+ android:id="@+id/label_error"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/input_hour"
+ android:layout_alignStart="@id/input_hour"
+ android:textColor="?attr/textColorError"
+ android:text="@string/time_picker_input_error" />
+ </RelativeLayout>
+ <Spinner
+ android:id="@+id/am_pm_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@id/input_block"
+ android:layout_alignParentEnd="true"/>
+
+</merge>
\ No newline at end of file
diff --git a/core/res/res/values-ldrtl/dimens.xml b/core/res/res/values-ldrtl/dimens.xml
deleted file mode 100644
index 807c042..0000000
--- a/core/res/res/values-ldrtl/dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <item type="dimen" format="integer" name="time_picker_column_start_material">1</item>
- <item type="dimen" format="integer" name="time_picker_column_end_material">0</item>
-</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 927988f..9824051 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -475,9 +475,6 @@
<dimen name="chooser_grid_padding">0dp</dimen>
- <item type="dimen" format="integer" name="time_picker_column_start_material">0</item>
- <item type="dimen" format="integer" name="time_picker_column_end_material">1</item>
-
<item type="dimen" name="aerr_padding_list_top">15dp</item>
<item type="dimen" name="aerr_padding_list_bottom">8dp</item>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index ebe577c..e3fdcec 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -149,6 +149,7 @@
<dimen name="timepicker_radial_picker_dimen">296dp</dimen>
<dimen name="timepicker_radial_picker_top_margin">16dp</dimen>
<dimen name="timepicker_radial_picker_horizontal_margin">16dp</dimen>
+ <dimen name="timepicker_edit_text_size">24sp</dimen>
<!-- Used by RadialTimePicker in clock-style TimePicker. -->
<dimen name="timepicker_selector_radius">20dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e00874f..19c5643 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4471,4 +4471,20 @@
<string name="device_storage_monitor_notification_channel">Device storage</string>
<!-- Channel name for UsbDeviceManager adb debugging notifications -->
<string name="adb_debugging_notification_channel_tv">USB debugging</string>
+
+ <!-- Label for the time picker hour input field. [CHAR LIMIT=20] -->
+ <string name="time_picker_hour_label">hour</string>
+ <!-- Label for the time picker minute input field. [CHAR LIMIT=20] -->
+ <string name="time_picker_minute_label">minute</string>
+ <!-- The title for the time picker dialog. [CHAR LIMIT=NONE] -->
+ <string name="time_picker_header_text">Set time</string>
+ <!-- Error shown to the user if they type in invalid hour or minute in the time picker. [CHAR LIMIT=NONE] -->
+ <string name="time_picker_input_error">Enter a valid time</string>
+ <!-- Label shown to the user in time picker to let them know that should type in time. [CHAR LIMIT=NONE] -->
+ <string name="time_picker_prompt_label">Type in time</string>
+ <!-- Accessibility string used for describing the button in time picker that changes the dialog to text input mode. [CHAR LIMIT=NONE] -->
+ <string name="time_picker_text_input_mode_description">Switch to text input mode for the time input.</string>
+ <!-- Accessibility string used for describing the button in time picker that changes the dialog to circular clock mode. [CHAR LIMIT=NONE] -->
+ <string name="time_picker_radial_mode_description">Switch to clock mode for the time input.</string>
+
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 1e15348..8f061a3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -426,6 +426,21 @@
<item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
</style>
+ <style name="TextAppearance.Material.TimePicker.InputHeader" parent="TextAppearance.Material">
+ <item name="textSize">@dimen/text_size_display_1_material</item>
+ <item name="textColor">@color/white</item>
+ <item name="fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="TextAppearance.Material.TimePicker.InputField" parent="TextAppearance.Material">
+ <item name="textSize">@dimen/timepicker_edit_text_size</item>
+ </style>
+
+ <style name="TextAppearance.Material.TimePicker.PromptLabel" parent="TextAppearance.Material">
+ <item name="textSize">@dimen/timepicker_text_size_normal</item>
+ <item name="fontFamily">sans-serif-medium</item>
+ </style>
+
<style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
<item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
<item name="textSize">@dimen/date_picker_year_label_size</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b95c20b..a420055 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2842,4 +2842,21 @@
<!-- Accessibility fingerprint gestures -->
<java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
<java-symbol type="string" name="capability_desc_canCaptureFingerprintGestures" />
+
+ <!-- Time picker -->
+ <java-symbol type="id" name="toggle_mode"/>
+ <java-symbol type="id" name="input_mode"/>
+ <java-symbol type="id" name="input_header"/>
+ <java-symbol type="id" name="input_separator"/>
+ <java-symbol type="id" name="input_hour"/>
+ <java-symbol type="id" name="input_minute"/>
+ <java-symbol type="id" name="am_pm_spinner"/>
+ <java-symbol type="id" name="label_hour"/>
+ <java-symbol type="id" name="label_minute"/>
+ <java-symbol type="id" name="label_error"/>
+ <java-symbol type="layout" name="time_picker_text_input_material"/>
+ <java-symbol type="drawable" name="btn_keyboard_key_material"/>
+ <java-symbol type="drawable" name="btn_event_material"/>
+ <java-symbol type="string" name="time_picker_text_input_mode_description"/>
+ <java-symbol type="string" name="time_picker_radial_mode_description"/>
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsTest.java b/core/tests/coretests/src/android/provider/SettingsTest.java
index d76980a..1ff2056 100644
--- a/core/tests/coretests/src/android/provider/SettingsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsTest.java
@@ -392,7 +392,6 @@
Settings.Secure.BACKUP_PROVISIONED,
Settings.Secure.BACKUP_TRANSPORT,
Settings.Secure.BLUETOOTH_HCI_LOG,
- Settings.Secure.BRIGHTNESS_USE_TWILIGHT, // Candidate for backup?
Settings.Secure.CARRIER_APPS_HANDLED,
Settings.Secure.COMPLETED_CATEGORY_PREFIX,
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
index dc60423..7e7e815 100644
--- a/core/tests/coretests/src/android/transition/FadeTransitionTest.java
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -21,7 +21,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.transition.Transition.TransitionListener;
-import android.transition.Transition.TransitionListenerAdapter;
+import android.transition.TransitionListenerAdapter;
import android.view.View;
import android.view.ViewGroup;
diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java
index a73f5a6..44fcd13 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTest.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTest.java
@@ -16,9 +16,17 @@
package android.view;
+import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.frameworks.coretests.R;
public class ViewAttachTest extends
ActivityInstrumentationTestCase2<ViewAttachTestActivity> {
@@ -51,4 +59,38 @@
SystemClock.sleep(250);
}
}
+
+ /**
+ * Make sure that on any attached view, if the view is full-screen and hosted
+ * on a round device, the round scrollbars will be displayed even if the activity
+ * window is offset.
+ *
+ * @throws Throwable
+ */
+ @UiThreadTest
+ public void testRoundScrollbars() throws Throwable {
+ final ViewAttachTestActivity activity = getActivity();
+ final View rootView = activity.getWindow().getDecorView();
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(
+ rootView.getWidth(),
+ rootView.getHeight(),
+ 50, /* xPosition */
+ 0, /* yPosition */
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+
+ rootView.setLayoutParams(params);
+
+ View contentView = activity.findViewById(R.id.view_attach_view);
+ boolean shouldDrawRoundScrollbars = contentView.shouldDrawRoundScrollbar();
+
+ if (activity.getResources().getConfiguration().isScreenRound()) {
+ assertTrue(shouldDrawRoundScrollbars);
+ } else {
+ // Never draw round scrollbars on non-round devices.
+ assertFalse(shouldDrawRoundScrollbars);
+ }
+ }
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c5961ab..039ab1f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -132,6 +132,7 @@
<permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <permission name="android.permission.BIND_IMS_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
@@ -339,4 +340,4 @@
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
-</permissions>
\ No newline at end of file
+</permissions>
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a799fdf..692199d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -192,7 +192,7 @@
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
hwui_c_includes += \
- $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
+ $(call intermediates-dir-for,STATIC_LIBRARIES,TARGET,) \
frameworks/rs/cpp \
frameworks/rs
endif
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index 37126a6..f69da48 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -29,5 +29,5 @@
libandroidfw
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
- LOCAL_SHARED_LIBRARIES += libRS libRScpp
+ LOCAL_SHARED_LIBRARIES += libRScpp
endif
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index d05e7f6..2ead5c5 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -24,6 +24,7 @@
#include <SkGaussianEdgeShader.h>
#include <SkPathOps.h>
#include <SkRRectsGaussianEdgeMaskFilter.h>
+#include <SkShadowUtils.h>
namespace android {
namespace uirenderer {
@@ -115,498 +116,6 @@
}
}
-/**
- * @param canvas the destination for the shadow draws
- * @param shape the shape casting the shadow
- * @param casterZValue the Z value of the caster RRect
- * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
- * @param draw the function used to draw 'shape'
- */
-template <typename Shape, typename F>
-static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
- float ambientAlpha, F&& draw) {
- if (ambientAlpha <= 0) {
- return;
- }
-
- const float kHeightFactor = 1.f/128.f;
- const float kGeomFactor = 64;
-
- float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
- float radius = casterZValue*kHeightFactor*kGeomFactor;
-
- sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
- SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setMaskFilter(std::move(mf));
- paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
-
- draw(shape, paint);
-}
-
-/**
- * @param canvas the destination for the shadow draws
- * @param shape the shape casting the shadow
- * @param casterZValue the Z value of the caster RRect
- * @param lightPos the position of the light casting the shadow
- * @param lightWidth
- * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
- * @param draw the function used to draw 'shape'
- */
-template <typename Shape, typename F>
-static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
- float spotAlpha, F&& draw) {
- if (spotAlpha <= 0) {
- return;
- }
-
- const Vector3 lightPos = SkiaPipeline::getLightCenter();
- float zRatio = casterZValue / (lightPos.z - casterZValue);
- // clamp
- if (zRatio < 0.0f) {
- zRatio = 0.0f;
- } else if (zRatio > 0.95f) {
- zRatio = 0.95f;
- }
-
- float blurRadius = SkiaPipeline::getLightRadius()*zRatio;
-
- SkAutoCanvasRestore acr(canvas, true);
-
- sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
- SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setMaskFilter(std::move(mf));
- paint.setARGB(spotAlpha, 0, 0, 0);
-
- // approximate projection by translating and scaling projected offset of bounds center
- // TODO: compute the actual 2D projection
- SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
- canvas->scale(scale, scale);
- SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
- SkMatrix ctmInverse;
- if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
- ALOGW("Matrix is degenerate. Will not render shadow!");
- return;
- }
- SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
- ctmInverse.mapPoints(&lightPos2D, 1);
- canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
-
- draw(shape, paint);
-}
-
-#define MAX_BLUR_RADIUS 16383.75f
-#define MAX_PAD 64
-
-/**
- * @param casterRect the rectangle bounds of the RRect casting the shadow
- * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
- * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
- * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
- * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range)
- * @param casterZValue the Z value of the caster RRect
- * @param scaleFactor the scale needed to map from src-space to device-space
- * @param canvas the destination for the shadow draws
- */
-static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
- SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
- SkScalar scaleFactor, SkCanvas* canvas) {
- SkASSERT(casterCornerRadius >= 0.0f);
-
- // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
- const SkScalar minRadius = 0.5f / scaleFactor;
-
- const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
- SkScalarHalf(casterRect.height()));
- const bool isRect = casterCornerRadius <= minRadius;
-
- sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
-
- if (ambientAlpha > 0.0f) {
- static const float kHeightFactor = 1.0f / 128.0f;
- static const float kGeomFactor = 64.0f;
-
- SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
- // the device-space radius sent to the blur shader must fit in 14.2 fixed point
- if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
- srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
- }
- const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
- const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
-
- // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
- // to get our stroke shape.
- SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
- minRadius);
-
- SkRRect ambientRRect;
- const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
- if (isOval) {
- ambientRRect = SkRRect::MakeOval(temp);
- } else if (isRect) {
- ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
- } else {
- ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
- casterCornerRadius + ambientPathOutset);
- }
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kStroke_Style);
- // we outset the stroke a little to cover up AA on the interior edge
- float pad = 0.5f;
- paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
- // handle scale of radius and pad due to CTM
- pad *= scaleFactor;
- const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
- SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
- SkASSERT(pad < MAX_PAD);
- // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
- // convert pad to 6.2 fixed point and place in the B component
- uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
- paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
- iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
-
- paint.setShader(edgeShader);
- canvas->drawRRect(ambientRRect, paint);
- }
-
- if (spotAlpha > 0.0f) {
- const Vector3 lightPos = SkiaPipeline::getLightCenter();
- float zRatio = casterZValue / (lightPos.z - casterZValue);
- // clamp
- if (zRatio < 0.0f) {
- zRatio = 0.0f;
- } else if (zRatio > 0.95f) {
- zRatio = 0.95f;
- }
-
- const SkScalar lightWidth = SkiaPipeline::getLightRadius();
- SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
- // the device-space radius sent to the blur shader must fit in 14.2 fixed point
- if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
- srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
- }
-
- SkRRect spotRRect;
- if (isOval) {
- spotRRect = SkRRect::MakeOval(casterRect);
- } else if (isRect) {
- spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
- } else {
- spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
- }
-
- SkRRect spotShadowRRect;
- // Compute the scale and translation for the spot shadow.
- const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
- spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
-
- SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
- spotShadowRRect.rect().centerY());
- SkMatrix ctmInverse;
- if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
- ALOGW("Matrix is degenerate. Will not render spot shadow!");
- return;
- }
- SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
- ctmInverse.mapPoints(&lightPos2D, 1);
- const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
- zRatio*(center.fY - lightPos2D.fY));
-
- SkAutoCanvasRestore acr(canvas, true);
-
- // We want to extend the stroked area in so that it meets up with the caster
- // geometry. The stroked geometry will, by definition already be inset half the
- // stroke width but we also have to account for the scaling.
- // We also add 1/2 to cover up AA on the interior edge.
- SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
- SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
- SkTAbs(casterRect.fBottom)));
- SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
- scaleOffset + 0.5f;
-
- // Compute area
- SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
- SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
- + spotShadowRRect.height());
- SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
- * (spotShadowRRect.width() + srcSpaceSpotRadius);
-
- SkPaint paint;
- paint.setAntiAlias(true);
-
- // If the area of the stroked geometry is larger than the fill geometry, just fill it.
- if (strokedArea > filledArea || casterAlpha < 1.0f || insetAmount < 0.0f) {
- paint.setStyle(SkPaint::kStrokeAndFill_Style);
- paint.setStrokeWidth(srcSpaceSpotRadius);
- } else {
- // Since we can't have unequal strokes, inset the shadow rect so the inner
- // and outer edges of the stroke will land where we want.
- SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
- SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
- minRadius);
- spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(strokeWidth);
- }
-
- // handle scale of radius and pad due to CTM
- const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
- SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
-
- const SkScalar devSpaceSpotPad = 0;
- SkASSERT(devSpaceSpotPad < MAX_PAD);
-
- // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
- // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
- uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
- paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
- iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
- paint.setShader(edgeShader);
-
- canvas->translate(spotOffset.fX, spotOffset.fY);
- canvas->drawRRect(spotShadowRRect, paint);
- }
-}
-
-/**
- * @param casterRect the rectangle bounds of the RRect casting the shadow
- * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
- * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
- * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
- * @param casterZValue the Z value of the caster RRect
- * @param scaleFactor the scale needed to map from src-space to device-space
- * @param clipRR the oval or rect with which the drawn roundrect must be intersected
- * @param canvas the destination for the shadow draws
- */
-static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
- SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
- const SkRRect& clipRR, SkCanvas* canvas) {
- SkASSERT(casterCornerRadius >= 0.0f);
-
- const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
- SkScalarHalf(casterRect.height()));
-
- if (ambientAlpha > 0.0f) {
- static const float kHeightFactor = 1.0f / 128.0f;
- static const float kGeomFactor = 64.0f;
-
- const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
- const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
-
- const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
- const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
-
- const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
- SkRect devSpaceAmbientRect;
- canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
-
- SkRRect devSpaceAmbientRRect;
- if (isOval) {
- devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
- } else {
- const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
- devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
- devSpaceCornerRadius);
- }
-
- const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
- SkRect devSpaceAmbClipRect;
- canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
- SkRRect devSpaceAmbientClipRR;
- if (clipRR.isOval()) {
- devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
- } else {
- SkASSERT(clipRR.isRect());
- devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
- }
-
- SkRect cover = srcSpaceAmbClipRect;
- if (!cover.intersect(srcSpaceAmbientRect)) {
- return;
- }
-
- SkPaint paint;
- paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
- paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceAmbientRRect,
- devSpaceAmbientClipRR, devSpaceAmbientRadius));
- canvas->drawRect(cover, paint);
- }
-
- if (spotAlpha > 0.0f) {
- const Vector3 lightPos = SkiaPipeline::getLightCenter();
- float zRatio = casterZValue / (lightPos.z - casterZValue);
- // clamp
- if (zRatio < 0.0f) {
- zRatio = 0.0f;
- } else if (zRatio > 0.95f) {
- zRatio = 0.95f;
- }
-
- const SkScalar lightWidth = SkiaPipeline::getLightRadius();
- const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
- const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
-
- // Compute the scale and translation for the spot shadow.
- const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
- const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
-
- SkRect srcSpaceScaledRect = casterRect;
- spotMatrix.mapRect(&srcSpaceScaledRect);
- srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
- SkScalarHalf(srcSpaceSpotRadius));
-
- SkRRect srcSpaceSpotRRect;
- if (isOval) {
- srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
- } else {
- srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
- casterCornerRadius * scale);
- }
-
- SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
- srcSpaceSpotRRect.rect().centerY());
- SkMatrix ctmInverse;
- if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
- ALOGW("Matrix is degenerate. Will not render spot shadow!");
- return;
- }
- SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
- ctmInverse.mapPoints(&lightPos2D, 1);
- const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
- zRatio*(center.fY - lightPos2D.fY));
-
- SkAutoCanvasRestore acr(canvas, true);
- canvas->translate(spotOffset.fX, spotOffset.fY);
-
- SkRect devSpaceScaledRect;
- canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
-
- SkRRect devSpaceSpotRRect;
- if (isOval) {
- devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
- } else {
- const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
- devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
- devSpaceScaledCornerRadius);
- }
-
- SkPaint paint;
- paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
-
- SkRect srcSpaceScaledClipRect = clipRR.rect();
- spotMatrix.mapRect(&srcSpaceScaledClipRect);
- srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
- SkScalarHalf(srcSpaceSpotRadius));
-
- SkRect devSpaceScaledClipRect;
- canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
- SkRRect devSpaceSpotClipRR;
- if (clipRR.isOval()) {
- devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
- } else {
- SkASSERT(clipRR.isRect());
- devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
- }
-
- paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceSpotRRect,
- devSpaceSpotClipRR, devSpaceSpotRadius));
-
- SkRect cover = srcSpaceScaledClipRect;
- if (!cover.intersect(srcSpaceSpotRRect.rect())) {
- return;
- }
-
- canvas->drawRect(cover, paint);
- }
-}
-
-/**
- * @param casterRect the rectangle bounds of the RRect casting the shadow
- * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
- * @param casterClipRect a rectangular clip that must be intersected with the
- * shadow-casting RRect prior to casting the shadow
- * @param revealClip a circular clip that must be interested with the castClipRect
- * and the shadow-casting rect prior to casting the shadow
- * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
- * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
- * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range)
- * @param casterZValue the Z value of the caster RRect
- * @param canvas the destination for the shadow draws
- *
- * We have special cases for 4 round rect shadow draws:
- * 1) a RRect clipped by a reveal animation
- * 2) a RRect clipped by a rectangle
- * 3) an unclipped RRect with non-uniform scale
- * 4) an unclipped RRect with uniform scale
- * 1,2 and 4 require that the scale is uniform.
- * 1 and 2 require that rects stay rects.
- */
-static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
- const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
- SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
- SkScalar scaleFactors[2];
- if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
- ALOGW("Matrix is degenerate. Will not render shadow!");
- return false;
- }
-
- // The casterClipRect will be empty when bounds clipping is disabled
- bool casterIsClippedByRect = !casterClipRect.isEmpty();
- bool uniformScale = scaleFactors[0] == scaleFactors[1];
-
- if (revealClip.willClip()) {
- if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
- return false; // Fall back to the slow path since PathOps are required
- }
-
- const float revealRadius = revealClip.getRadius();
- SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
- revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
- revealClip.getY()+revealRadius);
- SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
-
- DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
- casterZValue, scaleFactors[0], revealClipRR, canvas);
- return true;
- }
-
- if (casterIsClippedByRect) {
- if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
- return false; // Fall back to the slow path since PathOps are required
- }
-
- SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
-
- DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
- casterZValue, scaleFactors[0], casterClipRR, canvas);
- return true;
- }
-
- // The fast path needs uniform scale
- if (!uniformScale) {
- SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
- DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
- [&](const SkRRect& rrect, const SkPaint& paint) {
- canvas->drawRRect(rrect, paint);
- });
- DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
- [&](const SkRRect& rrect, const SkPaint& paint) {
- canvas->drawRRect(rrect, paint);
- });
- return true;
- }
-
- DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
- casterZValue, scaleFactors[0], canvas);
- return true;
-}
-
// copied from FrameBuilder::deferShadow
void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
const RenderProperties& casterProperties = caster->getNodeProperties();
@@ -626,8 +135,8 @@
return;
}
- float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha;
- float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha;
+ float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha;
+ float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha;
const float casterZValue = casterProperties.getZ();
const RevealClip& revealClip = casterProperties.getRevealClip();
@@ -659,19 +168,7 @@
hwuiMatrix.copyTo(shadowMatrix);
canvas->concat(shadowMatrix);
- const Outline& casterOutline = casterProperties.getOutline();
- Rect possibleRect;
- float radius;
- if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
- if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
- ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
- return;
- }
- }
-
- // Hard cases and calls to general shadow code
const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
-
// holds temporary SkPath to store the result of intersections
SkPath tmpPath;
const SkPath* casterPath = casterOutlinePath;
@@ -691,16 +188,11 @@
Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
casterPath = &tmpPath;
}
-
- DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
- [&](const SkPath& path, const SkPaint& paint) {
- canvas->drawPath(path, paint);
- });
-
- DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
- [&](const SkPath& path, const SkPaint& paint) {
- canvas->drawPath(path, paint);
- });
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
+ SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
+ SkShadowUtils::DrawShadow(canvas, *casterPath, casterZValue, skiaLightPos,
+ SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
+ casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
}; // namespace skiapipeline
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 0d567f7..7dfc2ee 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -80,6 +80,7 @@
delete mStencil;
mStencil = nullptr;
+ destroyLayersInUpdater();
GpuMemoryTracker::onGpuContextDestroyed();
}
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index a44fa9d..f78bf7a 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -99,8 +99,6 @@
mActiveLayerUpdaters.erase(layerUpdater);
}
- void destroyLayersInUpdater();
-
// TODO: This system is a little clunky feeling, this could use some
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
@@ -121,6 +119,7 @@
private:
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();
+ void destroyLayersInUpdater();
explicit RenderState(renderthread::RenderThread& thread);
~RenderState();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5a7de1d..a53e5e0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -570,7 +570,6 @@
}
mRenderPipeline->onDestroyHardwareResources();
}
- mRenderThread.renderState().destroyLayersInUpdater();
}
void CanvasContext::trimMemory(RenderThread& thread, int level) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 454ce4d..c2c2f22 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -603,6 +603,11 @@
}
void VulkanManager::swapBuffers(VulkanSurface* surface) {
+ if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
+ ATRACE_NAME("Finishing GPU work");
+ mDeviceWaitIdle(mBackendContext->fDevice);
+ }
+
VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
surface->mCurrentBackbufferIndex;
GrVkImageInfo* imageInfo;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 391a905..ce58a9c 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -913,13 +913,13 @@
}
}
/**
- * @hide
- * CANDIDATE FOR PUBLIC (or at least SYSTEM) API
* Returns the stream type matching the given attributes for volume control.
* Use this method to derive the stream type needed to configure the volume
- * control slider in an {@link Activity} with {@link Activity#setVolumeControlStream(int)}.
+ * control slider in an {@link android.app.Activity} with
+ * {@link android.app.Activity#setVolumeControlStream(int)}.
* <BR>Do not use this method to set the stream type on an audio player object
- * (e.g. {@link AudioTrack}, {@link MediaPlayer}), use <code>AudioAttributes</code> instead.
+ * (e.g. {@link AudioTrack}, {@link MediaPlayer}) as this is deprecated,
+ * use <code>AudioAttributes</code> instead.
* @param aa non-null AudioAttributes.
* @return a valid stream type for <code>Activity</code> or stream volume control that matches
* the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 2f6e260..a146c62 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.content.Intent;
+import android.media.MediaDescription;
import android.media.Rating;
import android.net.Uri;
import android.os.Bundle;
@@ -49,6 +50,10 @@
void onRepeatMode(int repeatMode);
void onShuffleMode(boolean enabled);
void onCustomAction(String action, in Bundle args);
+ void onAddQueueItem(in MediaDescription description);
+ void onAddQueueItemAt(in MediaDescription description, int index);
+ void onRemoveQueueItem(in MediaDescription description);
+ void onRemoveQueueItemAt(int index);
// These callbacks are for volume handling
void onAdjustVolume(int direction);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e92758c..7b5233a 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
@@ -51,6 +52,11 @@
MediaMetadata getMetadata();
PlaybackState getPlaybackState();
ParceledListSlice getQueue();
+ void addQueueItem(in MediaDescription description);
+ void addQueueItemAt(in MediaDescription description, int index);
+ void removeQueueItem(in MediaDescription description);
+ void removeQueueItemAt(int index);
+
CharSequence getQueueTitle();
Bundle getExtras();
int getRatingType();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 8cbf8e1..bab2af2 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -23,6 +23,7 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
+import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -38,6 +39,7 @@
import android.view.KeyEvent;
import java.lang.ref.WeakReference;
+import java.lang.UnsupportedOperationException;
import java.util.ArrayList;
import java.util.List;
@@ -111,8 +113,7 @@
}
/**
- * Get a {@link TransportControls} instance to send transport actions to
- * the associated session.
+ * Get a {@link TransportControls} instance to send transport actions to this session.
*
* @return A transport controls instance.
*/
@@ -151,7 +152,7 @@
try {
return mSessionBinder.getPlaybackState();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPlaybackState.", e);
+ Log.wtf(TAG, "Error calling getPlaybackState", e);
return null;
}
}
@@ -165,7 +166,7 @@
try {
return mSessionBinder.getMetadata();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getMetadata.", e);
+ Log.wtf(TAG, "Error calling getMetadata", e);
return null;
}
}
@@ -183,12 +184,103 @@
return queue.getList();
}
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getQueue.", e);
+ Log.wtf(TAG, "Error calling getQueue", e);
}
return null;
}
/**
+ * Add a queue item from the given {@code description} at the end of the play queue
+ * of this session. Not all sessions may support this.
+ *
+ * @param description The {@link MediaDescription} for creating the
+ * {@link MediaSession.QueueItem} to be inserted.
+ * @throws UnsupportedOperationException If this session doesn't support this.
+ * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
+ */
+ public void addQueueItem(MediaDescription description) {
+ try {
+ long flags = mSessionBinder.getFlags();
+ if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+ throw new UnsupportedOperationException(
+ "This session doesn't support queue management operations");
+ }
+ mSessionBinder.addQueueItem(description);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling addQueueItem", e);
+ }
+ }
+
+ /**
+ * Add a queue item from the given {@code description} at the specified position
+ * in the play queue of this session. Shifts the queue item currently at that position
+ * (if any) and any subsequent queue items to the right (adds one to their indices).
+ * Not all sessions may support this.
+ *
+ * @param description The {@link MediaDescription} for creating the
+ * {@link MediaSession.QueueItem} to be inserted.
+ * @param index The index at which the created {@link MediaSession.QueueItem} is to be inserted.
+ * @throws UnsupportedOperationException If this session doesn't support this.
+ * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
+ */
+ public void addQueueItem(MediaDescription description, int index) {
+ try {
+ long flags = mSessionBinder.getFlags();
+ if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+ throw new UnsupportedOperationException(
+ "This session doesn't support queue management operations");
+ }
+ mSessionBinder.addQueueItemAt(description, index);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling addQueueItemAt", e);
+ }
+ }
+
+ /**
+ * Remove the first occurrence of the specified {@link MediaSession.QueueItem}
+ * with the given {@link MediaDescription description} in the play queue of the associated
+ * session. Not all sessions may support this.
+ *
+ * @param description The {@link MediaDescription} for denoting the
+ * {@link MediaSession.QueueItem} to be removed.
+ * @throws UnsupportedOperationException If this session doesn't support this.
+ * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
+ */
+ public void removeQueueItem(MediaDescription description) {
+ try {
+ long flags = mSessionBinder.getFlags();
+ if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+ throw new UnsupportedOperationException(
+ "This session doesn't support queue management operations");
+ }
+ mSessionBinder.removeQueueItem(description);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling removeQueueItem", e);
+ }
+ }
+
+ /**
+ * Remove an queue item at the specified position in the play queue
+ * of this session. Not all sessions may support this.
+ *
+ * @param index The index of the element to be removed.
+ * @throws UnsupportedOperationException If this session doesn't support this.
+ * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
+ */
+ public void removeQueueItemAt(int index) {
+ try {
+ long flags = mSessionBinder.getFlags();
+ if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+ throw new UnsupportedOperationException(
+ "This session doesn't support queue management operations");
+ }
+ mSessionBinder.removeQueueItemAt(index);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling removeQueueItemAt", e);
+ }
+ }
+
+ /**
* Get the queue title for this session.
*/
public @Nullable CharSequence getQueueTitle() {
@@ -230,7 +322,7 @@
try {
return mSessionBinder.getRatingType();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRatingType.", e);
+ Log.wtf(TAG, "Error calling getRatingType", e);
return Rating.RATING_NONE;
}
}
@@ -245,7 +337,7 @@
try {
return mSessionBinder.getRepeatMode();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRepeatMode.", e);
+ Log.wtf(TAG, "Error calling getRepeatMode", e);
return PlaybackState.REPEAT_MODE_NONE;
}
}
@@ -259,7 +351,7 @@
try {
return mSessionBinder.isShuffleModeEnabled();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling isShuffleModeEnabled.", e);
+ Log.wtf(TAG, "Error calling isShuffleModeEnabled", e);
return false;
}
}
@@ -273,7 +365,7 @@
try {
return mSessionBinder.getFlags();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getFlags.", e);
+ Log.wtf(TAG, "Error calling getFlags", e);
}
return 0;
}
@@ -290,7 +382,7 @@
result.maxVolume, result.currentVolume);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getAudioInfo.", e);
+ Log.wtf(TAG, "Error calling getAudioInfo", e);
}
return null;
}
@@ -305,7 +397,7 @@
try {
return mSessionBinder.getLaunchPendingIntent();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPendingIntent.", e);
+ Log.wtf(TAG, "Error calling getPendingIntent", e);
}
return null;
}
@@ -334,7 +426,7 @@
try {
mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling setVolumeTo.", e);
+ Log.wtf(TAG, "Error calling setVolumeTo", e);
}
}
@@ -355,7 +447,7 @@
try {
mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
+ Log.wtf(TAG, "Error calling adjustVolumeBy", e);
}
}
@@ -421,7 +513,7 @@
try {
mSessionBinder.sendCommand(command, args, cb);
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCommand.", e);
+ Log.d(TAG, "Dead object in sendCommand", e);
}
}
@@ -435,7 +527,7 @@
try {
mPackageName = mSessionBinder.getPackageName();
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in getPackageName.", e);
+ Log.d(TAG, "Dead object in getPackageName", e);
}
}
return mPackageName;
@@ -452,7 +544,7 @@
try {
mTag = mSessionBinder.getTag();
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in getTag.", e);
+ Log.d(TAG, "Dead object in getTag", e);
}
}
return mTag;
@@ -652,7 +744,7 @@
try {
mSessionBinder.prepare();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare.", e);
+ Log.wtf(TAG, "Error calling prepare", e);
}
}
@@ -671,12 +763,12 @@
public void prepareFromMediaId(String mediaId, Bundle extras) {
if (TextUtils.isEmpty(mediaId)) {
throw new IllegalArgumentException(
- "You must specify a non-empty String for prepareFromMediaId.");
+ "You must specify a non-empty String for prepareFromMediaId");
}
try {
mSessionBinder.prepareFromMediaId(mediaId, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
+ Log.wtf(TAG, "Error calling prepare(" + mediaId + ")", e);
}
}
@@ -702,7 +794,7 @@
try {
mSessionBinder.prepareFromSearch(query, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
+ Log.wtf(TAG, "Error calling prepare(" + query + ")", e);
}
}
@@ -721,12 +813,12 @@
public void prepareFromUri(Uri uri, Bundle extras) {
if (uri == null || Uri.EMPTY.equals(uri)) {
throw new IllegalArgumentException(
- "You must specify a non-empty Uri for prepareFromUri.");
+ "You must specify a non-empty Uri for prepareFromUri");
}
try {
mSessionBinder.prepareFromUri(uri, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
+ Log.wtf(TAG, "Error calling prepare(" + uri + ")", e);
}
}
@@ -737,7 +829,7 @@
try {
mSessionBinder.play();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play.", e);
+ Log.wtf(TAG, "Error calling play", e);
}
}
@@ -751,12 +843,12 @@
public void playFromMediaId(String mediaId, Bundle extras) {
if (TextUtils.isEmpty(mediaId)) {
throw new IllegalArgumentException(
- "You must specify a non-empty String for playFromMediaId.");
+ "You must specify a non-empty String for playFromMediaId");
}
try {
mSessionBinder.playFromMediaId(mediaId, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
+ Log.wtf(TAG, "Error calling play(" + mediaId + ")", e);
}
}
@@ -778,7 +870,7 @@
try {
mSessionBinder.playFromSearch(query, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + query + ").", e);
+ Log.wtf(TAG, "Error calling play(" + query + ")", e);
}
}
@@ -792,12 +884,12 @@
public void playFromUri(Uri uri, Bundle extras) {
if (uri == null || Uri.EMPTY.equals(uri)) {
throw new IllegalArgumentException(
- "You must specify a non-empty Uri for playFromUri.");
+ "You must specify a non-empty Uri for playFromUri");
}
try {
mSessionBinder.playFromUri(uri, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+ Log.wtf(TAG, "Error calling play(" + uri + ")", e);
}
}
@@ -809,7 +901,7 @@
try {
mSessionBinder.skipToQueueItem(id);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
+ Log.wtf(TAG, "Error calling skipToItem(" + id + ")", e);
}
}
@@ -821,7 +913,7 @@
try {
mSessionBinder.pause();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling pause.", e);
+ Log.wtf(TAG, "Error calling pause", e);
}
}
@@ -833,7 +925,7 @@
try {
mSessionBinder.stop();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling stop.", e);
+ Log.wtf(TAG, "Error calling stop", e);
}
}
@@ -846,7 +938,7 @@
try {
mSessionBinder.seekTo(pos);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling seekTo.", e);
+ Log.wtf(TAG, "Error calling seekTo", e);
}
}
@@ -858,7 +950,7 @@
try {
mSessionBinder.fastForward();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling fastForward.", e);
+ Log.wtf(TAG, "Error calling fastForward", e);
}
}
@@ -869,7 +961,7 @@
try {
mSessionBinder.next();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling next.", e);
+ Log.wtf(TAG, "Error calling next", e);
}
}
@@ -881,7 +973,7 @@
try {
mSessionBinder.rewind();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rewind.", e);
+ Log.wtf(TAG, "Error calling rewind", e);
}
}
@@ -892,7 +984,7 @@
try {
mSessionBinder.previous();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling previous.", e);
+ Log.wtf(TAG, "Error calling previous", e);
}
}
@@ -907,7 +999,7 @@
try {
mSessionBinder.rate(rating);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rate.", e);
+ Log.wtf(TAG, "Error calling rate", e);
}
}
@@ -923,7 +1015,7 @@
try {
mSessionBinder.repeatMode(repeatMode);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling setRepeatMode.", e);
+ Log.wtf(TAG, "Error calling setRepeatMode", e);
}
}
@@ -936,7 +1028,7 @@
try {
mSessionBinder.shuffleMode(enabled);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling shuffleMode.", e);
+ Log.wtf(TAG, "Error calling shuffleMode", e);
}
}
@@ -950,7 +1042,7 @@
public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
@Nullable Bundle args) {
if (customAction == null) {
- throw new IllegalArgumentException("CustomAction cannot be null.");
+ throw new IllegalArgumentException("CustomAction cannot be null");
}
sendCustomAction(customAction.getAction(), args);
}
@@ -966,12 +1058,12 @@
*/
public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
if (TextUtils.isEmpty(action)) {
- throw new IllegalArgumentException("CustomAction cannot be null.");
+ throw new IllegalArgumentException("CustomAction cannot be null");
}
try {
mSessionBinder.sendCustomAction(action, args);
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCustomAction.", e);
+ Log.d(TAG, "Dead object in sendCustomAction", e);
}
}
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 84dc93a..bee3f52 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -87,6 +87,12 @@
public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
/**
+ * Set this flag on the session to indicate that it handles queue
+ * management commands through its {@link Callback}.
+ */
+ public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
+
+ /**
* System only flag for a session that needs to have priority over all other
* sessions. This flag ensures this session will receive media button events
* regardless of the current ordering in the system.
@@ -100,6 +106,7 @@
@IntDef(flag = true, value = {
FLAG_HANDLES_MEDIA_BUTTONS,
FLAG_HANDLES_TRANSPORT_CONTROLS,
+ FLAG_HANDLES_QUEUE_COMMANDS,
FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
public @interface SessionFlags { }
@@ -645,6 +652,22 @@
postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
}
+ private void dispatchAddQueueItem(MediaDescription description) {
+ postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM, description);
+ }
+
+ private void dispatchAddQueueItem(MediaDescription description, int index) {
+ postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index);
+ }
+
+ private void dispatchRemoveQueueItem(MediaDescription description) {
+ postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM, description);
+ }
+
+ private void dispatchRemoveQueueItemAt(int index) {
+ postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index);
+ }
+
private void dispatchMediaButton(Intent mediaButtonIntent) {
postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
}
@@ -666,10 +689,22 @@
postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
}
+ private void postToCallback(int what, int arg1) {
+ postToCallback(what, null, arg1);
+ }
+
private void postToCallback(int what, Object obj) {
postToCallback(what, obj, null);
}
+ private void postToCallback(int what, Object obj, int arg1) {
+ synchronized (mLock) {
+ if (mCallback != null) {
+ mCallback.post(what, obj, arg1);
+ }
+ }
+ }
+
private void postToCallback(int what, Object obj, Bundle extras) {
synchronized (mLock) {
if (mCallback != null) {
@@ -1043,6 +1078,47 @@
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
+
+ /**
+ * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
+ * {@link MediaDescription description} at the end of the play queue.
+ *
+ * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
+ * inserted.
+ */
+ public void onAddQueueItem(MediaDescription description) {
+ }
+
+ /**
+ * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
+ * {@link MediaDescription description} at the specified position in the play queue.
+ *
+ * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
+ * inserted.
+ * @param index The index at which the created {@link QueueItem} is to be inserted.
+ */
+ public void onAddQueueItem(MediaDescription description, int index) {
+ }
+
+ /**
+ * Called when a {@link MediaController} wants to remove the first occurrence of the
+ * specified {@link QueueItem} with the given {@link MediaDescription description}
+ * in the play queue.
+ *
+ * @param description The {@link MediaDescription} for denoting the {@link QueueItem} to be
+ * removed.
+ */
+ public void onRemoveQueueItem(MediaDescription description) {
+ }
+
+ /**
+ * Called when a {@link MediaController} wants to remove a {@link QueueItem} at the
+ * specified position in the play queue.
+ *
+ * @param index The index of the element to be removed.
+ */
+ public void onRemoveQueueItemAt(int index) {
+ }
}
/**
@@ -1239,6 +1315,38 @@
}
@Override
+ public void onAddQueueItem(MediaDescription description) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchAddQueueItem(description);
+ }
+ }
+
+ @Override
+ public void onAddQueueItemAt(MediaDescription description, int index) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchAddQueueItem(description, index);
+ }
+ }
+
+ @Override
+ public void onRemoveQueueItem(MediaDescription description) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchRemoveQueueItem(description);
+ }
+ }
+
+ @Override
+ public void onRemoveQueueItemAt(int index) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchRemoveQueueItemAt(index);
+ }
+ }
+
+ @Override
public void onAdjustVolume(int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1376,6 +1484,10 @@
private static final int MSG_CUSTOM_ACTION = 22;
private static final int MSG_ADJUST_VOLUME = 23;
private static final int MSG_SET_VOLUME = 24;
+ private static final int MSG_ADD_QUEUE_ITEM = 25;
+ private static final int MSG_ADD_QUEUE_ITEM_AT = 26;
+ private static final int MSG_REMOVE_QUEUE_ITEM = 27;
+ private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
private MediaSession.Callback mCallback;
@@ -1465,7 +1577,7 @@
mCallback.onSetRating((Rating) msg.obj);
break;
case MSG_REPEAT_MODE:
- mCallback.onSetRepeatMode((int) msg.obj);
+ mCallback.onSetRepeatMode(msg.arg1);
break;
case MSG_SHUFFLE_MODE:
mCallback.onSetShuffleModeEnabled((boolean) msg.obj);
@@ -1473,12 +1585,24 @@
case MSG_CUSTOM_ACTION:
mCallback.onCustomAction((String) msg.obj, msg.getData());
break;
+ case MSG_ADD_QUEUE_ITEM:
+ mCallback.onAddQueueItem((MediaDescription) msg.obj);
+ break;
+ case MSG_ADD_QUEUE_ITEM_AT:
+ mCallback.onAddQueueItem((MediaDescription) msg.obj, msg.arg1);
+ break;
+ case MSG_REMOVE_QUEUE_ITEM:
+ mCallback.onRemoveQueueItem((MediaDescription) msg.obj);
+ break;
+ case MSG_REMOVE_QUEUE_ITEM_AT:
+ mCallback.onRemoveQueueItemAt(msg.arg1);
+ break;
case MSG_ADJUST_VOLUME:
synchronized (mLock) {
vp = mVolumeProvider;
}
if (vp != null) {
- vp.onAdjustVolume((int) msg.obj);
+ vp.onAdjustVolume(msg.arg1);
}
break;
case MSG_SET_VOLUME:
@@ -1486,7 +1610,7 @@
vp = mVolumeProvider;
}
if (vp != null) {
- vp.onSetVolumeTo((int) msg.obj);
+ vp.onSetVolumeTo(msg.arg1);
}
break;
}
diff --git a/opengl/java/android/opengl/GLU.java b/opengl/java/android/opengl/GLU.java
index ed64556..ef9bf167 100644
--- a/opengl/java/android/opengl/GLU.java
+++ b/opengl/java/android/opengl/GLU.java
@@ -203,8 +203,8 @@
* @param view the current view, {x, y, width, height}
* @param viewOffset the offset into the view array where the view vector
* data starts.
- * @param obj the output vector {objX, objY, objZ}, that returns the
- * computed object coordinates.
+ * @param obj the output vector {objX, objY, objZ, objW}, that returns the
+ * computed homogeneous object coordinates.
* @param objOffset the offset into the obj array where the obj vector data
* starts.
* @return A return value of GL10.GL_TRUE indicates success, a return value
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 8bbc8c9..4ddafac 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1399,9 +1399,6 @@
Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
dumpSetting(s, p,
- Settings.Secure.BRIGHTNESS_USE_TWILIGHT,
- SecureSettingsProto.BRIGHTNESS_USE_TWILIGHT);
- dumpSetting(s, p,
Settings.Secure.ENABLED_VR_LISTENERS,
SecureSettingsProto.ENABLED_VR_LISTENERS);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 92a9da9..c2686f8 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -73,7 +73,7 @@
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone" />
+ android:visibility="invisible" />
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index a9ac2d9..27070ed 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -87,6 +87,7 @@
* above.
*/
private final Class<?>[] SERVICES_PER_USER = new Class[] {
+ Dependency.class,
Recents.class,
PipUI.class
};
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index aa22618..7c15096 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
@@ -25,10 +26,7 @@
import android.os.SystemClock;
import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIApplication;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.doze.DozeProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -45,6 +43,7 @@
Context context = dozeService;
SensorManager sensorManager = context.getSystemService(SensorManager.class);
PowerManager powerManager = context.getSystemService(PowerManager.class);
+ AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
DozeHost host = getHost(dozeService);
AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
@@ -57,7 +56,7 @@
machine.setParts(new DozeMachine.Part[]{
createDozeTriggers(context, sensorManager, host, config, params, handler, wakeLock,
machine),
- createDozeUi(context, host, wakeLock, machine),
+ createDozeUi(context, host, wakeLock, machine, handler, alarmManager),
});
return machine;
@@ -72,7 +71,7 @@
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
- DozeMachine machine) {
+ DozeMachine machine, Handler handler, AlarmManager alarmManager) {
if (mDozePlugin != null) {
DozeProvider.DozeUi dozeUi = mDozePlugin.provideDozeUi(context,
pluginMachine(context, machine, host),
@@ -82,7 +81,7 @@
pluginState(newState));
};
} else {
- return new DozeUi(context, machine, wakeLock, host);
+ return new DozeUi(context, alarmManager, machine, wakeLock, host, handler);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 00d2298..f3fb1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -30,15 +30,12 @@
void stopDozing();
void dozeTimeTick();
boolean isPowerSaveActive();
- boolean isNotificationLightOn();
boolean isPulsingBlocked();
void startPendingIntentDismissingKeyguard(PendingIntent intent);
interface Callback {
- default void onNewNotifications() {}
default void onNotificationHeadsUp() {}
- default void onNotificationLight(boolean on) {}
default void onPowerSaveChanged(boolean active) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 95e49ce..76e0283 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -16,7 +16,13 @@
package com.android.systemui.doze;
+import android.app.AlarmManager;
import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
/**
* The policy controlling doze.
@@ -24,16 +30,25 @@
public class DozeUi implements DozeMachine.Part {
private final Context mContext;
+ private final AlarmManager mAlarmManager;
private final DozeHost mHost;
- private DozeFactory.WakeLock mWakeLock;
- private DozeMachine mMachine;
+ private final Handler mHandler;
+ private final DozeFactory.WakeLock mWakeLock;
+ private final DozeMachine mMachine;
+ private final AlarmManager.OnAlarmListener mTimeTick;
- public DozeUi(Context context, DozeMachine machine, DozeFactory.WakeLock wakeLock,
- DozeHost host) {
+ private boolean mTimeTickScheduled = false;
+
+ public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
+ DozeFactory.WakeLock wakeLock, DozeHost host, Handler handler) {
mContext = context;
+ mAlarmManager = alarmManager;
mMachine = machine;
mWakeLock = wakeLock;
mHost = host;
+ mHandler = handler;
+
+ mTimeTick = this::onTimeTick;
}
private void pulseWhileDozing(int reason) {
@@ -54,6 +69,12 @@
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
+ case DOZE_AOD:
+ scheduleTimeTick();
+ break;
+ case DOZE:
+ unscheduleTimeTick();
+ break;
case DOZE_REQUEST_PULSE:
pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
break;
@@ -62,7 +83,52 @@
break;
case FINISH:
mHost.stopDozing();
+ unscheduleTimeTick();
break;
}
}
+
+ private void scheduleTimeTick() {
+ if (mTimeTickScheduled) {
+ return;
+ }
+
+ long delta = roundToNextMinute(System.currentTimeMillis()) - System.currentTimeMillis();
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler);
+
+ mTimeTickScheduled = true;
+ }
+
+ private void unscheduleTimeTick() {
+ if (!mTimeTickScheduled) {
+ return;
+ }
+ mAlarmManager.cancel(mTimeTick);
+ }
+
+ private long roundToNextMinute(long timeInMillis) {
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTimeInMillis(timeInMillis);
+ calendar.set(Calendar.MILLISECOND, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.add(Calendar.MINUTE, 1);
+
+ return calendar.getTimeInMillis();
+ }
+
+ private void onTimeTick() {
+ if (!mTimeTickScheduled) {
+ // Alarm was canceled, but we still got the callback. Ignore.
+ return;
+ }
+
+ mHost.dozeTimeTick();
+
+ // Keep wakelock until a frame has been pushed.
+ mHandler.post(mWakeLock.wrap(() -> {}));
+
+ mTimeTickScheduled = false;
+ scheduleTimeTick();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 3afbc35..dea56aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -237,6 +237,8 @@
i.setPackage(mComponent.getPackageName());
i = resolveIntent(i);
if (i != null) {
+ i.putExtra(TileService.EXTRA_COMPONENT, mComponent);
+ i.putExtra(TileService.EXTRA_STATE, mTile.getState());
return i;
}
return new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 9157e33..e635162 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -205,7 +205,6 @@
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
- getComponent(CommandQueue.class).addCallbacks(this);
UiModeManager uiModeManager = (UiModeManager) mContext.
getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
@@ -234,6 +233,7 @@
if (sSystemServicesProxy.isSystemUser(processUser)) {
// For the system user, initialize an instance of the interface that we can pass to the
// secondary user
+ getComponent(CommandQueue.class).addCallbacks(this);
mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
} else {
// For the secondary user, bind to the primary user's service to get a persistent
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index b9a0f74..0b09acc 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -72,6 +72,10 @@
return mMinimized;
}
+ public boolean isHomeStackResizable() {
+ return mHomeStackResizable;
+ }
+
private void addDivider(Configuration configuration) {
mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
@@ -119,6 +123,7 @@
mView.post(new Runnable() {
@Override
public void run() {
+ mHomeStackResizable = isHomeStackResizable;
if (mMinimized != minimized) {
mMinimized = minimized;
updateTouchable();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 49035ba..6f59fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1241,7 +1241,8 @@
public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) {
int dockSide = mWindowManagerProxy.getDockSide();
- if (dockSide != WindowManager.DOCKED_INVALID && !mDockedStackMinimized) {
+ if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
+ || !mDockedStackMinimized)) {
startDragging(false /* animate */, false /* touching */);
SnapTarget target = dockSideTopLeft(dockSide)
? mSnapAlgorithm.getDismissEndTarget()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 4b1baa2..0bd6491 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -105,7 +105,7 @@
private boolean mDimmed;
private boolean mDark;
- private int mBgTint = NO_COLOR;
+ protected int mBgTint = NO_COLOR;
private float mBgAlpha = 1f;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 477701c..9245df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -63,9 +63,6 @@
private static final int MSG_SET_WINDOW_STATE = 12 << MSG_SHIFT;
private static final int MSG_SHOW_RECENT_APPS = 13 << MSG_SHIFT;
private static final int MSG_HIDE_RECENT_APPS = 14 << MSG_SHIFT;
- private static final int MSG_BUZZ_BEEP_BLINKED = 15 << MSG_SHIFT;
- private static final int MSG_NOTIFICATION_LIGHT_OFF = 16 << MSG_SHIFT;
- private static final int MSG_NOTIFICATION_LIGHT_PULSE = 17 << MSG_SHIFT;
private static final int MSG_SHOW_SCREEN_PIN_REQUEST = 18 << MSG_SHIFT;
private static final int MSG_APP_TRANSITION_PENDING = 19 << MSG_SHIFT;
private static final int MSG_APP_TRANSITION_CANCELLED = 20 << MSG_SHIFT;
@@ -121,9 +118,6 @@
default void toggleKeyboardShortcutsMenu(int deviceId) { }
default void cancelPreloadRecentApps() { }
default void setWindowState(int window, int state) { }
- default void buzzBeepBlinked() { }
- default void notificationLightOff() { }
- default void notificationLightPulse(int argb, int onMillis, int offMillis) { }
default void showScreenPinningRequest(int taskId) { }
default void appTransitionPending() { }
default void appTransitionCancelled() { }
@@ -313,26 +307,6 @@
}
}
- public void buzzBeepBlinked() {
- synchronized (mLock) {
- mHandler.removeMessages(MSG_BUZZ_BEEP_BLINKED);
- mHandler.sendEmptyMessage(MSG_BUZZ_BEEP_BLINKED);
- }
- }
-
- public void notificationLightOff() {
- synchronized (mLock) {
- mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
- }
- }
-
- public void notificationLightPulse(int argb, int onMillis, int offMillis) {
- synchronized (mLock) {
- mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
- .sendToTarget();
- }
- }
-
public void showScreenPinningRequest(int taskId) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_SHOW_SCREEN_PIN_REQUEST, taskId, 0, null)
@@ -524,21 +498,6 @@
mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2);
}
break;
- case MSG_BUZZ_BEEP_BLINKED:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).buzzBeepBlinked();
- }
- break;
- case MSG_NOTIFICATION_LIGHT_OFF:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).notificationLightOff();
- }
- break;
- case MSG_NOTIFICATION_LIGHT_PULSE:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).notificationLightPulse((Integer) msg.obj, msg.arg1, msg.arg2);
- }
- break;
case MSG_SHOW_SCREEN_PIN_REQUEST:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showScreenPinningRequest(msg.arg1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 85e2bd6..2e9c7fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1880,14 +1880,14 @@
} else if (isUserLocked()) {
return mChildrenContainer.getGroupExpandFraction();
}
- } else if (isColorized()) {
+ } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
return -1.0f;
}
return 0.0f;
}
private boolean isColorized() {
- return mIsColorized;
+ return mIsColorized && mBgTint != NO_COLOR;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index e8e9d4e..2425076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -68,6 +68,7 @@
private float mMaxShelfEnd;
private int mRelativeOffset;
private boolean mInteractive;
+ private boolean mAnimationsEnabled = true;
public NotificationShelf(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -388,12 +389,13 @@
View rowIcon = row.getNotificationIcon();
float notificationIconPosition = row.getTranslationY() + row.getContentTranslation();
- if (usingLinearInterpolation) {
+ boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
+ if (usingLinearInterpolation && !stayingInShelf) {
// If we interpolate from the notification position, this might lead to a slightly
// odd interpolation, since the notification position changes as well. Let's interpolate
// from a fixed distance. We can only do this if we don't animate and the icon is
// always in the interpolated positon.
- notificationIconPosition = mMaxShelfEnd - getIntrinsicHeight() - iconTransformDistance;
+ notificationIconPosition = getTranslationY() - iconTransformDistance;
}
float notificationIconSize = 0.0f;
int iconTopPadding;
@@ -426,7 +428,7 @@
iconState.hidden = transitionAmount == 0.0f;
iconState.alpha = alpha;
iconState.yTranslation = iconYTranslation;
- if (row.isInShelf() && !row.isTransformingIntoShelf()) {
+ if (stayingInShelf) {
iconState.iconAppearAmount = 1.0f;
iconState.alpha = 1.0f;
iconState.scaleX = 1.0f;
@@ -573,6 +575,15 @@
mMaxShelfEnd = maxShelfEnd;
}
+ public void setAnimationsEnabled(boolean enabled) {
+ mAnimationsEnabled = enabled;
+ mCollapsedIcons.setAnimationsEnabled(enabled);
+ if (!enabled) {
+ // we need to wait with enabling the animations until the first frame has passed
+ mShelfIcons.setAnimationsEnabled(false);
+ }
+ }
+
private class ShelfState extends ExpandableViewState {
private float openedAmount;
private boolean hasItemsInStableShelf;
@@ -585,6 +596,7 @@
setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
+ mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
}
@Override
@@ -594,6 +606,7 @@
setOpenedAmount(openedAmount);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
+ mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 03c7325..e5f32df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -105,6 +105,11 @@
}
@Override
+ protected boolean shouldClearBackgroundOnReapply() {
+ return false;
+ }
+
+ @Override
public int getCustomBackgroundColor() {
int customBackgroundColor = super.getCustomBackgroundColor();
if (customBackgroundColor == 0 && mShowingLegacyBackground) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 836482a..5f5e1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -93,7 +93,9 @@
public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
mDarkInitialized = false;
Drawable background = mView.getBackground();
- mBackgroundColor = 0;
+ if (shouldClearBackgroundOnReapply()) {
+ mBackgroundColor = 0;
+ }
if (background instanceof ColorDrawable) {
mBackgroundColor = ((ColorDrawable) background).getColor();
mView.setBackground(null);
@@ -101,6 +103,10 @@
mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
}
+ protected boolean shouldClearBackgroundOnReapply() {
+ return true;
+ }
+
private boolean isColorLight(int backgroundColor) {
return Color.alpha(backgroundColor) == 0
|| ColorUtils.calculateLuminance(backgroundColor) > 0.5;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c25a45c..571ae26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -104,6 +104,7 @@
private float mOpenedAmount = 0.0f;
private float mVisualOverflowAdaption;
private boolean mDisallowNextAnimation;
+ private boolean mAnimationsEnabled = true;
public NotificationIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -255,7 +256,7 @@
iconState.visibleState = StatusBarIconView.STATE_ICON;
if (firstOverflowIndex == -1 && (isAmbient
|| (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) {
- firstOverflowIndex = noOverflowAfter ? i - 1 : i;
+ firstOverflowIndex = noOverflowAfter && !isAmbient ? i - 1 : i;
int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding;
visualOverflowStart = overflowStart + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT)
- totalDotLength / 2
@@ -422,6 +423,20 @@
return mIconSize;
}
+ public void setAnimationsEnabled(boolean enabled) {
+ if (!enabled && mAnimationsEnabled) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ ViewState childState = mIconStates.get(child);
+ if (childState != null) {
+ childState.cancelAnimations(child);
+ childState.applyToView(child);
+ }
+ }
+ }
+ mAnimationsEnabled = enabled;
+ }
+
public class IconState extends ViewState {
public float iconAppearAmount = 1.0f;
public float clampedAppearAmount = 1.0f;
@@ -438,52 +453,59 @@
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- if (justAdded) {
- super.applyToView(icon);
- icon.setAlpha(0.0f);
- icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */);
- animationProperties = ADD_ICON_PROPERTIES;
- animate = true;
- } else if (visibleState != icon.getVisibleState()) {
- animationProperties = DOT_ANIMATION_PROPERTIES;
- animate = true;
- }
- if (!animate && mAddAnimationStartIndex >= 0
- && indexOfChild(view) >= mAddAnimationStartIndex
- && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
- || visibleState != StatusBarIconView.STATE_HIDDEN)) {
- animationProperties = DOT_ANIMATION_PROPERTIES;
- animate = true;
- }
- if (needsCannedAnimation) {
- AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
- animationFilter.reset();
- animationFilter.combineFilter(ICON_ANIMATION_PROPERTIES.getAnimationFilter());
- mTempProperties.resetCustomInterpolators();
- mTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
- if (animationProperties != null) {
- animationFilter.combineFilter(animationProperties.getAnimationFilter());
- mTempProperties.combineCustomInterpolators(animationProperties);
+ boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation;
+ if (animationsAllowed) {
+ if (justAdded) {
+ super.applyToView(icon);
+ if (iconAppearAmount != 0.0f) {
+ icon.setAlpha(0.0f);
+ icon.setVisibleState(StatusBarIconView.STATE_HIDDEN,
+ false /* animate */);
+ animationProperties = ADD_ICON_PROPERTIES;
+ animate = true;
+ }
+ } else if (visibleState != icon.getVisibleState()) {
+ animationProperties = DOT_ANIMATION_PROPERTIES;
+ animate = true;
}
- animationProperties = mTempProperties;
- animationProperties.setDuration(CANNED_ANIMATION_DURATION);
- animate = true;
- mCannedAnimationStartIndex = indexOfChild(view);
+ if (!animate && mAddAnimationStartIndex >= 0
+ && indexOfChild(view) >= mAddAnimationStartIndex
+ && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+ || visibleState != StatusBarIconView.STATE_HIDDEN)) {
+ animationProperties = DOT_ANIMATION_PROPERTIES;
+ animate = true;
+ }
+ if (needsCannedAnimation) {
+ AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+ animationFilter.reset();
+ animationFilter.combineFilter(
+ ICON_ANIMATION_PROPERTIES.getAnimationFilter());
+ mTempProperties.resetCustomInterpolators();
+ mTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
+ if (animationProperties != null) {
+ animationFilter.combineFilter(animationProperties.getAnimationFilter());
+ mTempProperties.combineCustomInterpolators(animationProperties);
+ }
+ animationProperties = mTempProperties;
+ animationProperties.setDuration(CANNED_ANIMATION_DURATION);
+ animate = true;
+ mCannedAnimationStartIndex = indexOfChild(view);
+ }
+ if (!animate && mCannedAnimationStartIndex >= 0
+ && indexOfChild(view) > mCannedAnimationStartIndex
+ && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
+ || visibleState != StatusBarIconView.STATE_HIDDEN)) {
+ AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+ animationFilter.reset();
+ animationFilter.animateX();
+ mTempProperties.resetCustomInterpolators();
+ animationProperties = mTempProperties;
+ animationProperties.setDuration(CANNED_ANIMATION_DURATION);
+ animate = true;
+ }
}
- if (!animate && mCannedAnimationStartIndex >= 0
- && indexOfChild(view) > mCannedAnimationStartIndex
- && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
- || visibleState != StatusBarIconView.STATE_HIDDEN)) {
- AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
- animationFilter.reset();
- animationFilter.animateX();
- mTempProperties.resetCustomInterpolators();
- animationProperties = mTempProperties;
- animationProperties.setDuration(CANNED_ANIMATION_DURATION);
- animate = true;
- }
- icon.setVisibleState(visibleState);
- if (animate && !mDisallowNextAnimation) {
+ icon.setVisibleState(visibleState, animationsAllowed);
+ if (animate) {
animateTo(icon, animationProperties);
} else {
super.applyToView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 638c12f..54c67d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -603,8 +603,6 @@
new ArraySet<>();
private long mLastVisibilityReportUptimeMs;
- private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
-
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
@@ -1501,7 +1499,7 @@
ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
} else {
Divider divider = getComponent(Divider.class);
- if (divider != null && divider.isMinimized()) {
+ if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
// Undocking from the minimized state is not supported
return false;
} else {
@@ -1894,7 +1892,6 @@
updateEmptyShadeView();
updateQsExpansionEnabled();
- mShadeUpdates.check();
// Let's also update the icons
mIconController.updateNotificationIcons(mNotificationData);
@@ -3071,24 +3068,6 @@
}
@Override // CommandQueue
- public void buzzBeepBlinked() {
- }
-
- @Override
- public void notificationLightOff() {
- if (mDozeServiceHost != null) {
- mDozeServiceHost.fireNotificationLight(false);
- }
- }
-
- @Override
- public void notificationLightPulse(int argb, int onMillis, int offMillis) {
- if (mDozeServiceHost != null) {
- mDozeServiceHost.fireNotificationLight(true);
- }
- }
-
- @Override // CommandQueue
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
final int oldVal = mSystemUiVisibility;
@@ -5059,44 +5038,9 @@
return mStatusBarKeyguardViewManager.isShowing();
}
- private final class ShadeUpdates {
- private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
- private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
-
- public void check() {
- mNewVisibleNotifications.clear();
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- for (int i = 0; i < activeNotifications.size(); i++) {
- final Entry entry = activeNotifications.get(i);
- final boolean visible = entry.row != null
- && entry.row.getVisibility() == View.VISIBLE;
- if (visible) {
- mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
- }
- }
- final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
- mVisibleNotifications.clear();
- mVisibleNotifications.addAll(mNewVisibleNotifications);
-
- // We have new notifications
- if (updates && mDozeServiceHost != null) {
- mDozeServiceHost.fireNewNotifications();
- }
- }
- }
-
private final class DozeServiceHost implements DozeHost {
- // Amount of time to allow to update the time shown on the screen before releasing
- // the wakelock. This timeout is design to compensate for the fact that we don't
- // currently have a way to know when time display contents have actually been
- // refreshed once we've finished rendering a new frame.
- private static final long PROCESSING_TIME = 500;
-
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- // Keeps the last reported state by fireNotificationLight.
- private boolean mNotificationLightOn;
-
@Override
public String toString() {
return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
@@ -5114,19 +5058,6 @@
}
}
- public void fireNotificationLight(boolean on) {
- mNotificationLightOn = on;
- for (Callback callback : mCallbacks) {
- callback.onNotificationLight(on);
- }
- }
-
- public void fireNewNotifications() {
- for (Callback callback : mCallbacks) {
- callback.onNewNotifications();
- }
- }
-
@Override
public void addCallback(@NonNull Callback callback) {
mCallbacks.add(callback);
@@ -5192,11 +5123,6 @@
}
@Override
- public boolean isNotificationLightOn() {
- return mNotificationLightOn;
- }
-
- @Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
StatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 6006d5a..11927729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2766,6 +2766,7 @@
private void updateNotificationAnimationStates() {
boolean running = mAnimationsEnabled || mPulsing;
+ mShelf.setAnimationsEnabled(running);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index ab562d1..5d11ef3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -42,104 +42,11 @@
private IStatusBarService mBarService;
@Override
- public void setIcon(String slot, StatusBarIcon icon) {
- }
-
- @Override
- public void removeIcon(String slot) {
- }
-
- public void removeNotification(String key, RankingMap ranking) {
- }
-
- @Override
- public void disable(int state1, int state2, boolean animate) {
- }
-
- @Override
- public void animateExpandNotificationsPanel() {
- }
-
- @Override
- public void animateCollapsePanels(int flags) {
- }
-
- @Override
- public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
- }
-
- @Override
- public void topAppWindowChanged(boolean visible) {
- }
-
- @Override
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
- }
-
- @Override // CommandQueue
- public void setWindowState(int window, int state) {
- }
-
- @Override // CommandQueue
- public void buzzBeepBlinked() {
- }
-
- @Override // CommandQueue
- public void notificationLightOff() {
- }
-
- @Override // CommandQueue
- public void notificationLightPulse(int argb, int onMillis, int offMillis) {
- }
-
- @Override
- public void animateExpandSettingsPanel(String subPanel) {
- }
-
- @Override
- public void showScreenPinningRequest(int taskId) {
- }
-
- @Override
- public void appTransitionPending() {
- }
-
- @Override
- public void appTransitionCancelled() {
- }
-
- @Override
- public void appTransitionStarting(long startTime, long duration) {
- }
-
- @Override
- public void appTransitionFinished() {
- }
-
- @Override
- public void onCameraLaunchGestureDetected(int source) {
- }
-
- @Override
public void showTvPictureInPictureMenu() {
PipManager.getInstance().showTvPictureInPictureMenu();
}
@Override
- public void addQsTile(ComponentName tile) {
- }
-
- @Override
- public void remQsTile(ComponentName tile) {
- }
-
- @Override
- public void clickTile(ComponentName tile) {
- }
-
- @Override
public void start() {
putComponent(TvStatusBar.class, this);
CommandQueue commandQueue = getComponent(CommandQueue.class);
@@ -160,8 +67,4 @@
}
}
- @Override
- public void handleSystemNavigationKey(int arg1) {
- // Not implemented
- }
}
diff --git a/packages/SystemUI/tests/res/layout/custom_view_dark.xml b/packages/SystemUI/tests/res/layout/custom_view_dark.xml
new file mode 100644
index 0000000..9e460a5
--- /dev/null
+++ b/packages/SystemUI/tests/res/layout/custom_view_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ff000000"
+ />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 43f8629..5b22986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -171,27 +171,6 @@
}
@Test
- public void testBuzzBeepBlink() {
- mCommandQueue.buzzBeepBlinked();
- waitForIdleSync();
- verify(mCallbacks).buzzBeepBlinked();
- }
-
- @Test
- public void testNotificationLightOff() {
- mCommandQueue.notificationLightOff();
- waitForIdleSync();
- verify(mCallbacks).notificationLightOff();
- }
-
- @Test
- public void testNotificationLightPulse() {
- mCommandQueue.notificationLightPulse(1, 2, 3);
- waitForIdleSync();
- verify(mCallbacks).notificationLightPulse(eq(1), eq(2), eq(3));
- }
-
- @Test
public void testScreenPinRequest() {
mCommandQueue.showScreenPinningRequest(1);
waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
new file mode 100644
index 0000000..d07cea1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.statusbar;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationViewWrapper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationCustomViewWrapperTest {
+
+ private Context mContext;
+ private ExpandableNotificationRow mRow;
+
+ @Before
+ @UiThreadTest
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mRow = new ExpandableNotificationRow(mContext, null);
+ }
+
+ @Test
+ public void testBackgroundPersists() {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.custom_view_dark);
+ View v = views.apply(mContext, null);
+ NotificationViewWrapper wrap = NotificationCustomViewWrapper.wrap(mContext, v, mRow);
+ wrap.notifyContentUpdated(null, false /* isLowPriority */);
+ Assert.assertTrue(wrap.getCustomBackgroundColor() != 0);
+ views.reapply(mContext, v);
+ wrap.notifyContentUpdated(null, false /* isLowPriority */);
+ Assert.assertTrue(wrap.getCustomBackgroundColor() != 0);
+ }
+
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 78436f7..178a697 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -34,7 +34,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -43,7 +42,6 @@
import android.provider.Settings;
import android.service.autofill.IAutoFillManagerService;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.LocalLog;
import android.util.Log;
import android.util.Slog;
@@ -72,66 +70,47 @@
public final class AutoFillManagerService extends SystemService {
private static final String TAG = "AutoFillManagerService";
- static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
+ static final boolean DEBUG = false;
- private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
-
- private static final int MSG_UNBIND = 1;
- private static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 2;
- private static final int MSG_REQUEST_AUTO_FILL = 3;
+ protected static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 1;
+ protected static final int MSG_REQUEST_AUTO_FILL = 2;
+ private static final int MSG_REQUEST_SAVE_FOR_USER = 3;
private static final int MSG_ON_VALUE_CHANGED = 4;
- private static final int MSG_REQUEST_SAVE_FOR_USER = 5;
- private final AutoFillManagerServiceStub mServiceStub;
private final Context mContext;
- private final ContentResolver mResolver;
+ private final AutoFillUI mUi;
private final Object mLock = new Object();
- private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
-
- @Override
- public void executeMessage(Message msg) {
- switch (msg.what) {
- case MSG_UNBIND: {
- synchronized (mLock) {
- removeCachedServiceLocked(msg.arg1);
- }
- return;
- } case MSG_REQUEST_AUTO_FILL_FOR_USER: {
- handleAutoFillForUser(msg.arg1);
- return;
- } case MSG_REQUEST_SAVE_FOR_USER: {
- handleSaveForUser(msg.arg1);
- return;
- } case MSG_REQUEST_AUTO_FILL: {
- final SomeArgs args = (SomeArgs) msg.obj;
- try {
- final int userId = msg.arg1;
- final int flags = msg.arg2;
- final IBinder activityToken = (IBinder) args.arg1;
- final AutoFillId autoFillId = (AutoFillId) args.arg2;
- final Rect bounds = (Rect) args.arg3;
- handleAutoFill(activityToken, userId, autoFillId, bounds, flags);
- } finally {
- args.recycle();
- }
- return;
- } case MSG_ON_VALUE_CHANGED: {
- final SomeArgs args = (SomeArgs) msg.obj;
- try {
- final int userId = msg.arg1;
- final IBinder activityToken = (IBinder) args.arg1;
- final AutoFillId autoFillId = (AutoFillId) args.arg2;
- final AutoFillValue newValue = (AutoFillValue) args.arg3;
- handleValueChanged(activityToken, userId, autoFillId, newValue);
- } finally {
- args.recycle();
- }
- return;
- } default: {
- Slog.w(TAG, "Invalid message: " + msg);
- }
+ private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
+ switch (msg.what) {
+ case MSG_REQUEST_AUTO_FILL_FOR_USER: {
+ handleAutoFillForUser(msg.arg1);
+ return;
+ } case MSG_REQUEST_SAVE_FOR_USER: {
+ handleSaveForUser(msg.arg1);
+ return;
+ } case MSG_REQUEST_AUTO_FILL: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final int userId = msg.arg1;
+ final int flags = msg.arg2;
+ final IBinder activityToken = (IBinder) args.arg1;
+ final AutoFillId autoFillId = (AutoFillId) args.arg2;
+ final Rect bounds = (Rect) args.arg3;
+ args.recycle();
+ handleAutoFill(activityToken, userId, autoFillId, bounds, flags);
+ return;
+ } case MSG_ON_VALUE_CHANGED: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final int userId = msg.arg1;
+ final IBinder activityToken = (IBinder) args.arg1;
+ final AutoFillId autoFillId = (AutoFillId) args.arg2;
+ final AutoFillValue newValue = (AutoFillValue) args.arg3;
+ args.recycle();
+ handleValueChanged(activityToken, userId, autoFillId, newValue);
+ return;
+ } default: {
+ Slog.w(TAG, "Invalid message: " + msg);
}
}
};
@@ -148,10 +127,10 @@
* Entries on this cache are added on demand and removed when:
* <ol>
* <li>An auto-fill service app is removed.
- * <li>The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change.
- * <li>It has not been interacted with for {@link #SERVICE_BINDING_LIFETIME_MS} ms.
+ * <li>The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change.\
* </ol>
*/
+ // TODO(b/33197203): Update the above comment
@GuardedBy("mLock")
private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
@@ -160,19 +139,14 @@
public AutoFillManagerService(Context context) {
super(context);
-
mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
-
mContext = context;
-
- mResolver = context.getContentResolver();
- mServiceStub = new AutoFillManagerServiceStub();
+ mUi = new AutoFillUI(mContext);
}
@Override
public void onStart() {
- if (DEBUG) Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE);
- publishBinderService(AUTO_FILL_MANAGER_SERVICE, mServiceStub);
+ publishBinderService(AUTO_FILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
}
@Override
@@ -186,61 +160,41 @@
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
final String componentName = Settings.Secure.getStringForUser(
- mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
+ mContext.getContentResolver(), 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);
+ Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
return null;
}
}
- if (DEBUG) {
- Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
- + serviceComponent + ", info: " + serviceInfo);
- }
if (serviceInfo == null) {
- if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
return null;
}
- return new AutoFillManagerServiceImpl(this, mContext, mLock, mRequestsHistory,
- userId, serviceInfo.applicationInfo.uid, serviceComponent,
- SERVICE_BINDING_LIFETIME_MS);
+
+ try {
+ return new AutoFillManagerServiceImpl(mContext, mLock, mRequestsHistory,
+ userId, serviceComponent, mUi);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Auto-fill service not found: " + serviceComponent, e);
+ }
+
+ return null;
}
/**
* Gets the service instance for an user.
- * <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.
*/
- // TODO(b/33197203): make private once AutoFillUi does not uses notifications
- AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
+ AutoFillManagerServiceImpl getOrCreateServiceForUserLocked(int userId) {
AutoFillManagerServiceImpl service = mServicesCache.get(userId);
- if (service != null) {
- if (DEBUG)
- Log.d(TAG, "reusing cached service for userId " + userId);
- service.setLifeExpectancy(SERVICE_BINDING_LIFETIME_MS);
- } else {
+ if (service == null) {
service = newServiceForUser(userId);
- if (service == null) {
- // Already logged
- return null;
- }
- if (DEBUG) Log.d(TAG, "creating new cached service for userId " + userId);
- service.startLocked();
mServicesCache.put(userId, service);
}
- // Keep service connection alive for a while, in case user needs to interact with it
- // (for example, to save the data that was inputted in)
- if (mHandlerCaller.hasMessages(MSG_UNBIND)) {
- mHandlerCaller.removeMessages(MSG_UNBIND);
- }
- mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessageI(MSG_UNBIND, userId),
- SERVICE_BINDING_LIFETIME_MS);
return service;
}
@@ -248,22 +202,17 @@
* Removes a cached service for a given user.
*/
void removeCachedServiceLocked(int userId) {
- if (DEBUG) Log.d(TAG, "removing cached service for userId " + userId);
final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
- if (service == null) {
- if (DEBUG) {
- Log.d(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
- }
- return;
+ if (service != null) {
+ mServicesCache.delete(userId);
+ service.destroyLocked();
}
- mServicesCache.delete(userId);
- service.stopLocked();
}
private void handleAutoFill(IBinder activityToken, int userId, AutoFillId autoFillId,
Rect bounds, int flags) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutoFillManagerServiceImpl service = getOrCreateServiceForUserLocked(userId);
if (service != null) {
service.requestAutoFillLocked(activityToken, autoFillId, bounds, flags);
}
@@ -273,7 +222,7 @@
private void handleValueChanged(IBinder activityToken, int userId, AutoFillId autoFillId,
AutoFillValue newValue) {
synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutoFillManagerServiceImpl service = getOrCreateServiceForUserLocked(userId);
if (service != null) {
service.onValueChangeLocked(activityToken, autoFillId, newValue);
}
@@ -283,45 +232,32 @@
private IBinder getTopActivityForUser() {
final List<IBinder> topActivities = LocalServices
.getService(ActivityManagerInternal.class).getTopVisibleActivities();
- if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
if (topActivities.isEmpty()) {
- Slog.w(TAG, "Could not get top activity");
return null;
}
return topActivities.get(0);
}
private void handleAutoFillForUser(int userId) {
- if (DEBUG) Slog.d(TAG, "handler.requestAutoFillForUser(): id=" + userId);
final IBinder activityToken = getTopActivityForUser();
- if (activityToken == null) {
- return;
+ if (activityToken != null) {
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service =
+ getOrCreateServiceForUserLocked(userId);
+ service.requestAutoFillLocked(activityToken, null, null, 0);
+ }
}
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service == null) {
- Slog.w(TAG, "no service for user " + userId);
- return;
- }
- service.requestAutoFillLocked(activityToken, null, null, 0);
- }
}
private void handleSaveForUser(int userId) {
- if (DEBUG) Slog.d(TAG, "handler.handleSaveForUser(): id=" + userId);
final IBinder activityToken = getTopActivityForUser();
- if (activityToken == null) {
- return;
- }
-
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service == null) {
- Slog.w(TAG, "no service for user " + userId);
- return;
+ if (activityToken != null) {
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service =
+ getOrCreateServiceForUserLocked(userId);
+ service.requestSaveForUserLocked(activityToken);
}
- service.requestSaveForUserLocked(activityToken);
}
}
@@ -347,9 +283,6 @@
@Override
public void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
- if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", autoFillId=" + id
- + ", bounds=" + bounds);
-
final IBinder activityToken = getTopActivity();
if (activityToken != null) {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIIOOO(MSG_REQUEST_AUTO_FILL,
@@ -360,25 +293,19 @@
@Override
public void requestAutoFillForUser(int userId) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-
- mHandlerCaller.sendMessage(
- mHandlerCaller.obtainMessageI(MSG_REQUEST_AUTO_FILL_FOR_USER, userId));
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI(
+ MSG_REQUEST_AUTO_FILL_FOR_USER, userId));
}
@Override
public void requestSaveForUser(int userId) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-
- mHandlerCaller.sendMessage(
- mHandlerCaller.obtainMessageI(MSG_REQUEST_SAVE_FOR_USER, userId));
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI(MSG_REQUEST_SAVE_FOR_USER, userId));
}
@Override
public void onValueChanged(AutoFillId id, AutoFillValue value) {
- if (DEBUG) Slog.d(TAG, "onValueChanged(): id=" + id + ", value=" + value);
-
final IBinder activityToken = getTopActivity();
-
if (activityToken != null) {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOOO(MSG_ON_VALUE_CHANGED,
UserHandle.getCallingUserId(), activityToken, id, value));
@@ -407,6 +334,7 @@
impl.dumpLocked(" ", pw);
}
}
+ mUi.dump(pw);
}
pw.println("Requests history:");
mRequestsHistory.reverseDump(fd, pw, args);
@@ -430,7 +358,6 @@
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
- if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
synchronized (mLock) {
removeCachedServiceLocked(userId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 42e4fd3..e32e21d 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -16,15 +16,11 @@
package com.android.server.autofill;
-import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_ERROR;
-import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_REQUESTED;
-import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_SUCCESS;
import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_SHOW;
import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_HIDE;
import static com.android.server.autofill.Helper.DEBUG;
import static com.android.server.autofill.Helper.VERBOSE;
-import static com.android.server.autofill.Helper.bundleToString;
import android.annotation.Nullable;
import android.app.Activity;
@@ -38,33 +34,26 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
+import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Rect;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintService;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
import android.os.Bundle;
-import android.os.DeadObjectException;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
import android.service.autofill.AutoFillService;
import android.service.autofill.AutoFillServiceInfo;
+import android.service.autofill.FillCallback;
import android.service.autofill.IAutoFillAppCallback;
-import android.service.autofill.IAutoFillServerCallback;
import android.service.autofill.IAutoFillService;
+import android.service.autofill.IFillCallback;
import android.service.voice.VoiceInteractionSession;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.LocalLog;
-import android.util.Log;
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.AutoFillValue;
import android.view.autofill.Dataset;
@@ -77,8 +66,6 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -95,24 +82,13 @@
private static int sSessionIdCounter = 0;
private final int mUserId;
- private final int mUid;
private final ComponentName mComponent;
private final String mComponentName;
private final Context mContext;
private final IActivityManager mAm;
private final Object mLock;
private final AutoFillServiceInfo mInfo;
- private final AutoFillManagerService mManagerService;
-
- // Token used for fingerprint authentication
- // TODO(b/33197203): create on demand?
- private final IBinder mAuthToken = new Binder();
-
- private final IFingerprintService mFingerprintService =
- IFingerprintService.Stub.asInterface(ServiceManager.getService("fingerprint"));
-
- @GuardedBy("mLock")
- private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
+ private final AutoFillUI mUi;
private final LocalLog mRequestsHistory;
@@ -122,15 +98,7 @@
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
final String reason = intent.getStringExtra("reason");
if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
-
- synchronized (mLock) {
- final int size = mSessions.size();
- for (int i = 0; i < size; i++) {
- final Session session = mSessions.valueAt(i);
- // TODO(b/33197203): invalidate the sessions instead?
- session.mUi.closeAll();
- }
- }
+ mUi.hideAll();
}
}
};
@@ -144,40 +112,7 @@
// TODO(b/33197203): need to make sure service is bound while callback is pending and/or
// use WeakReference
@GuardedBy("mLock")
- private static final SparseArray<Session> mSessions = new SparseArray<>();
-
- private final ServiceConnection mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Slog.d(TAG, "onServiceConnected():" + name);
- synchronized (mLock) {
- mService = IAutoFillService.Stub.asInterface(service);
- try {
- mService.onConnected();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception on service.onConnected(): " + e);
- return;
- }
- if (!mQueuedRequests.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size());
- }
- for (final QueuedRequest request: mQueuedRequests) {
- requestAutoFillLocked(request.activityToken, request.autoFillId,
- request.bounds, request.flags, false);
- }
- mQueuedRequests.clear();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Slog.d(TAG, name + " disconnected");
- synchronized (mLock) {
- mService = null;
- mManagerService.removeCachedServiceLocked(mUserId);
- }
- }
- };
+ private final SparseArray<Session> mSessions = new SparseArray<>();
/**
* Receiver of assist data from the app's {@link Activity}, uses the {@code resultData} as
@@ -193,8 +128,13 @@
Slog.w(TAG, "no app callback on mAssistReceiver's resultData");
return;
}
+
final AssistStructure structure = resultData
.getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
+ if (structure == null) {
+ Slog.w(TAG, "no assist structure for id " + resultCode);
+ return;
+ }
final Session session;
synchronized (mLock) {
@@ -203,91 +143,55 @@
Slog.w(TAG, "no server callback for id " + resultCode);
return;
}
- session.setAppCallbackLocked(appBinder);
- // TODO(b/33197203): since service is fetching the data (to use for save later),
- // we should optimize what's sent (for example, remove layout containers,
- // color / font info, etc...)
- session.mStructure = structure;
-
- // TODO(b/33197203, b/33269702): Must fetch the data so it's available later on
- // handleSave(), even if if the activity is gone by then, but structure.ensureData()
- // gives a ONE_WAY warning because system_service could block on app calls.
- // We need to change AssistStructure so it provides a "one-way" writeToParcel()
- // method that sends all the data
- structure.ensureData();
-
- structure.sanitizeForParceling(true);
- if (VERBOSE) {
- Slog.v(TAG, "Dumping " + structure + " before calling service.autoFill()");
- structure.dump();
- }
- mService.autoFill(structure, session.mServerCallback);
}
+
+ // TODO(b/33197203): since service is fetching the data (to use for save later),
+ // we should optimize what's sent (for example, remove layout containers,
+ // color / font info, etc...)
+
+ // TODO(b/33197203, b/33269702): Must fetch the data so it's available later on
+ // handleSave(), even if if the activity is gone by then, but structure.ensureData()
+ // gives a ONE_WAY warning because system_service could block on app calls.
+ // We need to change AssistStructure so it provides a "one-way" writeToParcel()
+ // method that sends all the data
+ structure.ensureData();
+
+ structure.sanitizeForParceling(true);
+
+ if (VERBOSE) {
+ Slog.v(TAG, "Dumping " + structure + " before calling service.autoFill()");
+ structure.dump();
+ }
+
+ session.onApplicationDataAvailable(structure, appBinder);
}
};
- @GuardedBy("mLock")
- private IAutoFillService mService;
- @GuardedBy("mLock")
- private boolean mBound;
- @GuardedBy("mLock")
- private boolean mValid;
-
- // Estimated time when the service will be evicted from the cache.
- long mEstimateTimeOfDeath;
-
- AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock,
- LocalLog requestsHistory, int userId, int uid, ComponentName component, long ttl) {
- mManagerService = managerService;
+ AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+ int userId, ComponentName component, AutoFillUI ui)
+ throws PackageManager.NameNotFoundException {
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
mUserId = userId;
- mUid = uid;
mComponent = component;
mComponentName = mComponent.flattenToShortString();
mAm = ActivityManager.getService();
- setLifeExpectancy(ttl);
+ mUi = ui;
+ mInfo = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
- final AutoFillServiceInfo info;
- try {
- info = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Auto-fill service not found: " + component, e);
- mInfo = null;
- mValid = false;
- return;
- }
- mInfo = info;
- if (mInfo.getParseError() != null) {
- Slog.w(TAG, "Bad auto-fill service: " + mInfo.getParseError());
- mValid = false;
- return;
- }
-
- mValid = true;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
}
- void setLifeExpectancy(long ttl) {
- mEstimateTimeOfDeath = SystemClock.uptimeMillis() + ttl;
- }
- void startLocked() {
- if (DEBUG) Slog.d(TAG, "startLocked()");
-
- final Intent intent = new Intent(AutoFillService.SERVICE_INTERFACE);
- intent.setComponent(mComponent);
- mBound = mContext.bindServiceAsUser(intent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUserId));
-
- if (!mBound) {
- Slog.w(TAG, "Failed binding to auto-fill service " + mComponent);
- return;
- }
- if (DEBUG) Slog.d(TAG, "Bound to " + mComponent);
+ /**
+ * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
+ */
+ void requestSaveForUserLocked(IBinder activityToken) {
+ final Session session = getOrCreateSessionByTokenLocked(activityToken);
+ session.onSaveLocked();
}
/**
@@ -299,66 +203,17 @@
* @param flags optional flags.
*/
void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
- @Nullable Rect bounds, int flags) {
- if (!mBound) {
- Slog.w(TAG, "requestAutoFillLocked() failed because it's not bound to service");
- return;
- }
-
- requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
- }
-
- /**
- * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
- */
- void requestSaveForUserLocked(IBinder activityToken) {
- if (!mBound) {
- Slog.w(TAG, "requestSaveForUserLocked() failed because it's not bound to service");
- return;
- }
- if (mService == null) {
- Slog.w(TAG, "requestSaveForUserLocked: service not set");
- return;
- }
-
- final Session session = getSessionByTokenLocked(activityToken);
- if (session == null) {
- Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
- return;
- }
-
- session.onSaveLocked();
- }
-
- private void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
- @Nullable Rect bounds, int flags, boolean queueIfNecessary) {
- if (mService == null) {
- if (!queueIfNecessary) {
- Slog.w(TAG, "requestAutoFillLocked(): service is null");
- return;
- }
- if (DEBUG) Slog.d(TAG, "requestAutoFillLocked(): service not set yet, queuing it");
- mQueuedRequests.add(new QueuedRequest(activityToken, autoFillId, bounds, flags));
- return;
- }
-
+ @Nullable Rect bounds, int flags) {
final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
+ " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
mRequestsHistory.log(historyItem);
// TODO(b/33197203): Handle partitioning
- Session session = getSessionByTokenLocked(activityToken);
-
- if (session == null) {
- session = createSessionByTokenLocked(activityToken);
- } else {
- if (DEBUG) Slog.d(TAG, "reusing session for " + activityToken + ": " + session.mId);
- }
-
+ Session session = getOrCreateSessionByTokenLocked(activityToken);
session.updateAutoFillInput(flags, autoFillId, null, bounds);
}
- private Session getSessionByTokenLocked(IBinder activityToken) {
+ private Session getOrCreateSessionByTokenLocked(IBinder activityToken) {
final int size = mSessions.size();
for (int i = 0; i < size; i++) {
final Session session = mSessions.valueAt(i);
@@ -366,14 +221,14 @@
return session;
}
}
- return null;
+ return createSessionByTokenLocked(activityToken);
}
private Session createSessionByTokenLocked(IBinder activityToken) {
final int sessionId = ++sSessionIdCounter;
if (DEBUG) Slog.d(TAG, "creating session for " + activityToken + ": " + sessionId);
- final Session newSession = new Session(sessionId, activityToken);
+ final Session newSession = new Session(mContext, activityToken, sessionId);
mSessions.put(sessionId, newSession);
/*
@@ -401,88 +256,33 @@
*/
void onValueChangeLocked(IBinder activityToken, AutoFillId autoFillId, AutoFillValue newValue) {
// TODO(b/33197203): add MetricsLogger call
- final Session session = getSessionByTokenLocked(activityToken);
- if (session == null) {
- Slog.w(TAG, "onValueChangeLocked(): session gone for " + activityToken);
- return;
- }
-
+ final Session session = getOrCreateSessionByTokenLocked(activityToken);
session.updateValueLocked(autoFillId, newValue);
}
- void stopLocked() {
- if (DEBUG) Slog.d(TAG, "stopLocked()");
-
- // Sanity check.
- if (mService == null) {
- Slog.w(TAG, "service already null on shutdown");
- return;
- }
- try {
- mService.onDisconnected();
- } catch (RemoteException e) {
- if (! (e instanceof DeadObjectException)) {
- Slog.w(TAG, "Exception calling service.onDisconnected(): " + e);
- }
- } finally {
- mService = null;
- }
-
- if (mBound) {
- mContext.unbindService(mConnection);
- mBound = false;
- }
- if (mValid) {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
- }
-
void removeSessionLocked(int id) {
if (DEBUG) Slog.d(TAG, "Removing session " + id);
- mSessions.remove(id);
+ mSessions.get(id);
+ }
- // TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback
- // and cached AssistStructures
+ void destroyLocked() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ Session session = mSessions.valueAt(i);
+ session.destroy();
+ mSessions.removeAt(i);
+ }
}
void dumpLocked(String prefix, PrintWriter pw) {
- if (!mValid) {
- pw.print(" NOT VALID: ");
- if (mInfo == null) {
- pw.println("no info");
- } else {
- pw.println(mInfo.getParseError());
- }
- 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(mComponentName);
- pw.print(prefix); pw.print("mService: "); pw.println(mService);
- pw.print(prefix); pw.print("mBound="); pw.println(mBound);
- pw.print(prefix); pw.print("mEstimateTimeOfDeath=");
- TimeUtils.formatDuration(mEstimateTimeOfDeath, SystemClock.uptimeMillis(), pw);
- pw.println();
- pw.print(prefix); pw.print("mAuthToken: "); pw.println(mAuthToken);
-
if (DEBUG) {
// ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
pw.print(prefix); pw.println("ServiceInfo:");
mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
}
- if (mQueuedRequests.isEmpty()) {
- pw.print(prefix); pw.println("No queued requests");
- } else {
- pw.print(prefix); pw.println("Queued requests:");
- 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("sSessionIdCounter="); pw.println(sSessionIdCounter);
final int size = mSessions.size();
if (size == 0) {
@@ -498,32 +298,10 @@
@Override
public String toString() {
- return "AutoFillManagerServiceImpl: [userId=" + mUserId + ", uid=" + mUid
+ return "AutoFillManagerServiceImpl: [userId=" + mUserId
+ ", component=" + mComponentName + "]";
}
- private static final class QueuedRequest {
- final IBinder activityToken;
- final AutoFillId autoFillId;
- final Rect bounds;
- final int flags;
-
- QueuedRequest(IBinder activityToken, AutoFillId autoFillId, Rect bounds, int flags) {
- this.activityToken = activityToken;
- this.autoFillId = autoFillId;
- this.bounds = bounds;
- this.flags = flags;
- }
-
- @Override
- public String toString() {
- if (!DEBUG) return super.toString();
-
- return "QueuedRequest: [flags=" + flags + ", token=" + activityToken
- + ", id=" + autoFillId + ", bounds=" + bounds;
- }
- }
-
/**
* State for a given view with a AutoFillId.
*
@@ -553,12 +331,7 @@
* Response should only be set once.
*/
void setResponse(FillResponse response) {
- if (mResponse != null) {
- Slog.e(TAG, "ViewState response set more than once");
- return;
- }
mResponse = response;
-
maybeCallOnFillReady();
}
@@ -608,39 +381,27 @@
// - On all authentication scenarios.
// - When user does not interact back after a while.
// - When service is unbound.
- final class Session implements ViewState.Listener {
+ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
+ AutoFillUI.AutoFillUiCallback {
+ private final int mId;
- private final AutoFillUI mUi;
private final WeakReference<IBinder> mActivityToken;
@GuardedBy("mLock")
private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
+
@GuardedBy("mLock")
@Nullable
private ViewState mCurrentViewState;
private IAutoFillAppCallback mAppCallback;
+ @GuardedBy("mLock")
+ RemoteFillService mRemoteFillService;
+
// TODO(b/33197203): Get a response per view instead of per activity.
@GuardedBy("mLock")
private FillResponse mCurrentResponse;
- @GuardedBy("mLock")
- private FillResponse mResponseRequiringAuth;
- @GuardedBy("mLock")
- private Dataset mDatasetRequiringAuth;
-
- /**
- * Used to auto-fill the activity directly when the FillCallback.onResponse() is called as
- * the result of a successful user authentication on service's side.
- */
- @GuardedBy("mLock")
- private boolean mAutoFillDirectly;
-
- /**
- * Used to remember which {@link Dataset} filled the session.
- */
- @GuardedBy("mLock")
- private Dataset mAutoFilledDataset;
/**
* Map of ids that must be updated so they're send to {@link #onSaveLocked()}.
@@ -652,168 +413,77 @@
* Assist structure sent by the app; it will be updated (sanitized, change values for save)
* before sent to {@link AutoFillService}.
*/
+ @GuardedBy("mLock")
private AssistStructure mStructure;
- // TODO(b/33197203): use handler to handle results?
- // TODO(b/33197203): handle all callback methods and/or cancelation?
- private IFingerprintServiceReceiver mServiceReceiver =
- new IFingerprintServiceReceiver.Stub() {
+ private Session(Context context, IBinder activityToken, int id) {
+ mActivityToken = new WeakReference<>(activityToken);
+ mRemoteFillService = new RemoteFillService(context, mComponent, mUserId, this);
+ mId = id;
+ }
- @Override
- public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.d(TAG, "onEnrollResult()");
+ // FillServiceCallbacks
+ @Override
+ public void onFillRequestSuccess(FillResponse response) {
+ // TODO(b/33197203): add MetricsLogger call
+ if (response == null) {
+ destroy();
+ return;
}
-
- @Override
- public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
- if (DEBUG) Slog.d(TAG, "onAcquired()");
+ synchronized (mLock) {
+ processResponseLocked(response);
}
+ }
- @Override
- public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
- if (DEBUG) Slog.d(TAG, "onAuthenticationSucceeded(): " + fp.getGroupId());
+ // FillServiceCallbacks
+ @Override
+ public void onFillRequestFailure(CharSequence message) {
+ // TODO(b/33197203): add MetricsLogger call
+ getUiForShowing().showError(message);
+ destroy();
+ }
- // First, check what was authenticated, a response or a dataset.
- // Then, decide how to handle it:
- // - If service provided data, handle them directly.
- // - Otherwise, notify service.
+ // FillServiceCallbacks
+ @Override
+ public void onSaveRequestSuccess() {
+ // TODO: Implement
+ }
- mAutoFillDirectly = true;
+ // FillServiceCallbacks
+ @Override
+ public void onSaveRequestFailure(CharSequence message) {
+ // TODO(b/33197203): add MetricsLogger call
+ getUiForShowing().showError(message);
+ destroy();
+ }
- if (mDatasetRequiringAuth != null) {
- if (mDatasetRequiringAuth.isEmpty()) {
- notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
- FLAG_AUTHENTICATION_SUCCESS);
- } else {
- autoFillApp(mDatasetRequiringAuth);
- }
- } else if (mResponseRequiringAuth != null) {
- final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
- if (datasets.isEmpty()) {
- notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
- FLAG_AUTHENTICATION_SUCCESS);
- } else {
- showResponseLocked(mResponseRequiringAuth, true);
- }
- } else {
- Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset");
- }
+ // FillServiceCallbacks
+ @Override
+ public void authenticate(IntentSender intent, Intent fillInIntent) {
+ startAuthIntent(intent, fillInIntent);
+ }
- mUi.dismissFingerprintRequest(true);
+ // FillServiceCallbacks
+ @Override
+ public void onServiceDied(RemoteFillService service) {
+ // TODO: Implement
+ }
+
+ // AutoFillUiCallback
+ @Override
+ public void fill(Dataset dataset) {
+ autoFill(dataset);
+ }
+
+ // AutoFillUiCallback
+ @Override
+ public void save() {
+ synchronized (mLock) {
+ onSaveLocked();
}
-
- @Override
- public void onAuthenticationFailed(long deviceId) {
- if (DEBUG) Slog.d(TAG, "onAuthenticationFailed()");
- // Do nothing - onError() will be called after a few failures...
- }
-
- @Override
- public void onError(long deviceId, int error, int vendorCode) {
- if (DEBUG) Slog.d(TAG, "onError()");
-
- // Notify service so it can fallback to its own authentication
- if (mDatasetRequiringAuth != null) {
- notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
- FLAG_AUTHENTICATION_ERROR);
- } else if (mResponseRequiringAuth != null) {
- notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
- FLAG_AUTHENTICATION_ERROR);
- } else {
- Slog.w(TAG, "onError(): no response or dataset");
- }
-
- mUi.dismissFingerprintRequest(false);
- }
-
- @Override
- public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.d(TAG, "onRemoved()");
- }
-
- @Override
- public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.d(TAG, "onEnumerated()");
- }
- };
-
- private IAutoFillServerCallback mServerCallback = new IAutoFillServerCallback.Stub() {
- @Override
- public void showResponse(FillResponse response) {
- // TODO(b/33197203): add MetricsLogger call
- if (response == null) {
- if (DEBUG) Slog.d(TAG, "showResponse(): null response");
-
- removeSelf();
- return;
- }
-
- synchronized (mLock) {
- showResponseLocked(response, response.isAuthRequired());
- }
- }
-
- @Override
- public void showError(CharSequence message) {
- // TODO(b/33197203): add MetricsLogger call
- if (DEBUG) Slog.d(TAG, "showError(): " + message);
-
- mUi.showError(message);
- removeSelf();
- }
-
- @Override
- public void onSaved() {
- // TODO(b/33197203): add MetricsLogger call
- if (DEBUG) Slog.d(TAG, "onSaved()");
-
- removeSelf();
- }
-
- @Override
- public void unlockFillResponse(int flags) {
- // TODO(b/33197203): add proper MetricsLogger calls?
- if (DEBUG) Log.d(TAG, "unlockUser(): flags=" + flags);
-
- synchronized (mLock) {
- if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
- if (mResponseRequiringAuth == null) {
- Log.wtf(TAG, "unlockUser(): no mResponseRequiringAuth on flags "
- + flags);
- removeSelf();
- return;
- }
- final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
- if (datasets.isEmpty()) {
- Log.w(TAG, "unlockUser(): no dataset on previous response: "
- + mResponseRequiringAuth);
- removeSelf();
- return;
- }
- mAutoFillDirectly = true;
- showResponseLocked(mResponseRequiringAuth, false);
- }
- // TODO(b/33197203): show UI error on authentication failure?
- // Or let service handle it?
- }
- }
-
- @Override
- public void unlockDataset(Dataset dataset, int flags) {
- // TODO(b/33197203): add proper MetricsLogger calls?
- if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags);
-
- if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
- autoFillApp(dataset != null ? dataset : mDatasetRequiringAuth);
- return;
- }
- }
- };
-
- final int mId;
+ }
private Session(int id, IBinder activityToken) {
- mUi = new AutoFillUI(mContext, this);
mId = id;
mActivityToken = new WeakReference<>(activityToken);
}
@@ -826,7 +496,7 @@
// TODO(b/33197203): ignore if not part of the savable ids.
if (mUpdatedValues == null) {
- // Lazy intializes it
+ // Lazy initializes it
mUpdatedValues = new HashMap<>();
}
mUpdatedValues.put(id, newValue);
@@ -847,32 +517,10 @@
if (mUpdatedValues == null || mUpdatedValues.isEmpty()) {
// Nothing changed
if (DEBUG) Slog.d(TAG, "onSave(): when no changes, comes no responsibilities");
-
return;
}
// TODO(b/33197203): make sure the extras are tested by CTS
- final Bundle responseExtras = mCurrentResponse == null ? null
- : mCurrentResponse.getExtras();
- final Bundle datasetExtras = mAutoFilledDataset == null ? null
- : mAutoFilledDataset.getExtras();
- final Bundle extras = (responseExtras == null && datasetExtras == null)
- ? null : new Bundle();
- if (responseExtras != null) {
- if (DEBUG) {
- Slog.d(TAG, "response extras on save extras: "
- + bundleToString(responseExtras));
- }
- extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
- }
- if (datasetExtras != null) {
- if (DEBUG) {
- Slog.d(TAG, "dataset extras on save extras: " + bundleToString(datasetExtras));
- }
- extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
- }
-
-
for (Entry<AutoFillId, AutoFillValue> entry : mUpdatedValues.entrySet()) {
final AutoFillId id = entry.getKey();
final ViewNode node = findViewNodeByIdLocked(id);
@@ -891,20 +539,24 @@
Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()");
mStructure.dump();
}
- try {
- mService.save(mStructure, mServerCallback, extras);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error calling save on service: " + e);
- // TODO(b/33197203): invalidate session?
- }
+
+ mRemoteFillService.onSaveRequest(mStructure, mCurrentResponse.getExtras());
}
- void setAppCallbackLocked(IBinder appBinder) {
+ void onApplicationDataAvailable(AssistStructure structure, IBinder appCallback) {
+ setAppCallback(appCallback);
+ mStructure = structure;
+ // TODO(b/33197203): Need to pipe the bundle
+ mRemoteFillService.onFillRequest(structure, null);
+ }
+
+ private void setAppCallback(IBinder appBinder) {
try {
appBinder.linkToDeath(() -> {
if (DEBUG) Slog.d(TAG, "app callback died");
// TODO(b/33197203): more cleanup here?
mAppCallback = null;
+ destroy();
}, 0);
} catch (RemoteException e) {
Slog.w(TAG, "linkToDeath() failed: " + e);
@@ -957,78 +609,105 @@
filterText = text.toString();
}
}
- mUi.showFillUi(viewState, response.getDatasets(), bounds, filterText);
+ getUiForShowing().showFillUi(viewState, response.getDatasets(), bounds, filterText);
}
- private void showResponseLocked(FillResponse response, boolean authRequired) {
- if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
- + ", authRequired=" + authRequired +"):" + response);
+ private void processResponseLocked(FillResponse response) {
+ if (DEBUG) Slog.d(TAG, "showResponse(authRequired="
+ + response.getAuthentication() +"):" + response);
- if (mAutoFillDirectly && response != null) {
- final List<Dataset> datasets = response.getDatasets();
- if (datasets.size() == 1) {
- // User authenticated and provider returned just 1 dataset - auto-fill it now!
- final Dataset dataset = datasets.get(0);
- if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset);
+ // TODO(b/33197203): add MetricsLogger calls
- autoFillApp(dataset);
- return;
- }
- }
+ mCurrentResponse = response;
- if (!authRequired) {
- // TODO(b/33197203): add MetricsLogger call
- mCurrentResponse = response;
+ if (mCurrentResponse.getAuthentication() != null) {
+ // ...or handle authentication.
+ Intent fillInIntent = createAuthFillInIntent(response.getId(), mStructure,
+ new Bundle(), new FillCallback(new IFillCallback.Stub() {
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ // TODO(b/33197203): Handle cancellation
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ mCurrentResponse = createAuthenticatedResponse(
+ mCurrentResponse, response);
+ processResponseLocked(mCurrentResponse);
+ }
+
+ @Override
+ public void onFailure(CharSequence message) {
+ getUiForShowing().showError(message);
+ destroy();
+ }
+ }));
+
+ getUiForShowing().showFillResponseAuthRequest(
+ mCurrentResponse.getAuthentication(), fillInIntent);
+ } else {
// TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design
if (mCurrentViewState != null) {
mCurrentViewState.setResponse(mCurrentResponse);
}
- return;
}
-
- // Handles response that requires authentication.
- // TODO(b/33197203): add MetricsLogger call, including if fingerprint requested
-
- mResponseRequiringAuth = response;
- final boolean requiresFingerprint = response.hasCryptoObject();
- if (requiresFingerprint) {
- // TODO(b/33197203): check if fingerprint is available first and call error callback
- // with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
- // Start scanning for the fingerprint.
- scanFingerprint(response.getCryptoObjectOpId());
- }
- // Displays the message asking the user to tap (or fingerprint) for AutoFill.
- mUi.showFillResponseAuthenticationRequest(requiresFingerprint,
- response.getExtras(), response.getFlags());
}
void autoFill(Dataset dataset) {
synchronized (mLock) {
// Autofill it directly...
- if (!dataset.isAuthRequired()) {
+ if (dataset.getAuthentication() == null) {
autoFillApp(dataset);
+ // For now just show this on every fill
+ getUiForShowing().showSaveUi();
return;
}
// ...or handle authentication.
-
- mDatasetRequiringAuth = dataset;
- final boolean requiresFingerprint = dataset.hasCryptoObject();
- if (requiresFingerprint) {
- // TODO(b/33197203): check if fingerprint is available first and call error
- // callback with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
- // Start scanning for the fingerprint.
- scanFingerprint(dataset.getCryptoObjectOpId());
- // Displays the message asking the user to tap (or fingerprint) for AutoFill.
- mUi.showDatasetFingerprintAuthenticationRequest(dataset);
- } else {
- try {
- mService.authenticateDataset(dataset.getExtras(),
- FLAG_AUTHENTICATION_REQUESTED);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error authenticating dataset: " + e);
+ Intent fillInIntent = createAuthFillInIntent(dataset.getId(), mStructure,
+ new Bundle(), new FillCallback(new IFillCallback.Stub() {
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ // TODO(b/33197203): Handle cancellation
}
- }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ mCurrentResponse = createAuthenticatedResponse(
+ mCurrentResponse, response);
+ Dataset augmentedDataset = Helper.findDatasetById(dataset.getId(),
+ mCurrentResponse);
+ if (augmentedDataset != null) {
+ autoFill(augmentedDataset);
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence message) {
+ getUiForShowing().showError(message);
+ destroy();
+ }
+ }));
+
+ startAuthIntent(dataset.getAuthentication(), fillInIntent);
+ }
+ }
+
+ private Intent createAuthFillInIntent(String itemId, AssistStructure structure,
+ Bundle extras, FillCallback fillCallback) {
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ITEM_ID, itemId);
+ fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ASSIST_STRUCTURE, structure);
+ fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_EXTRAS, extras);
+ fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_CALLBACK, fillCallback);
+ return fillInIntent;
+ }
+
+ private void startAuthIntent(IntentSender intent, Intent fillInIntent) {
+ try {
+ mAppCallback.startIntentSender(intent, fillInIntent);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error launching auth intent", e);
}
}
@@ -1036,11 +715,6 @@
pw.print(prefix); pw.print("mId: "); pw.println(mId);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get());
pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
- pw.print(prefix);
- pw.print("mResponseRequiringAuth: "); pw.println(mResponseRequiringAuth);
- pw.print(prefix);
- pw.print("mDatasetRequiringAuth: "); pw.println(mDatasetRequiringAuth);
- pw.print(prefix); pw.print("mAutoFillDirectly: "); pw.println(mAutoFillDirectly);
pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState);
pw.print(prefix); pw.print("mViewStates: "); pw.println(mViewStates.size());
final String prefix2 = prefix + " ";
@@ -1057,42 +731,8 @@
} else {
pw.println("null");
}
- }
- /**
- * Notifies the result of a {@link FillResponse} authentication request to the service.
- *
- * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
- * used the fingerprint sensors to authenticate.
- */
- void notifyResponseAuthenticationResult(Bundle extras, int flags) {
- if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
- + ", extras=" + bundleToString(extras));
- synchronized (mLock) {
- try {
- mService.authenticateFillResponse(extras, flags);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending authentication result back to service: " + e);
- }
- }
- }
-
- /**
- * Notifies the result of a {@link Dataset} authentication request to the service.
- *
- * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
- * it gets the results from a fingerprint authentication.
- */
- void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
- if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
- + ", extras=" + bundleToString(extras));
- synchronized (mLock) {
- try {
- mService.authenticateDataset(extras, flags);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending authentication result back to service: " + e);
- }
- }
+ mRemoteFillService.dump(prefix, pw);
}
void autoFillApp(Dataset dataset) {
@@ -1106,32 +746,9 @@
}
}
- /**
- * Called by UI to trigger a save request to the service.
- */
- void requestSave() {
- synchronized (mLock) {
- onSaveLocked();
- }
- }
-
- private void scanFingerprint(long opId) {
- // TODO(b/33197203): add MetricsLogger call
- if (DEBUG) Slog.d(TAG, "Starting fingerprint scan for op id: " + opId);
-
- // TODO(b/33197203): since we're clearing the AutoFillService's identity, make sure
- // this method is only called at the proper times, otherwise a malicious provider could
- // keep the callback refence to bypass the check
- final long token = Binder.clearCallingIdentity();
- try {
- // TODO(b/33197203): set a timeout?
- mFingerprintService.authenticate(mAuthToken, opId, mUserId, mServiceReceiver, 0,
- null);
- } catch (RemoteException e) {
- // Local call, shouldn't happen.
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ private AutoFillUI getUiForShowing() {
+ mUi.setCallback(this, mId);
+ return mUi;
}
private ViewNode findViewNodeByIdLocked(AutoFillId id) {
@@ -1167,10 +784,89 @@
return null;
}
- private void removeSelf() {
+ private void destroy() {
synchronized (mLock) {
+ mRemoteFillService.destroy();
+ mUi.hideAll();
+ mUi.setCallback(null, 0);
removeSessionLocked(mId);
}
}
+
+ /**
+ * Creates a response from the {@code original} and an {@code update} by
+ * replacing all items that needed authentication (response or datasets)
+ * with their updated version if the latter does not need authentication.
+ * New datasets that don't require auth are appended.
+ *
+ * @param original The original response requiring auth at some level.
+ * @param update An updated response with auth not needed anymore at some level.
+ * @return A new response with updated items where auth is not needed anymore.
+ */
+ // TODO(b/33197203) Unit test
+ FillResponse createAuthenticatedResponse(FillResponse original, FillResponse update) {
+ // Can update only if ids match
+ if (!original.getId().equals(update.getId())) {
+ return original;
+ }
+
+ // If the original required auth and the update doesn't, the update wins
+ // but only if none of the update's datasets requires authentication.
+ if (original.getAuthentication() != null && update.getAuthentication() == null) {
+ ArraySet<Dataset> updateDatasets = update.getDatasets();
+ final int udpateDatasetCount = updateDatasets.size();
+ for (int i = 0; i < udpateDatasetCount; i++) {
+ Dataset updateDataset = updateDatasets.valueAt(i);
+ if (updateDataset.getAuthentication() != null) {
+ return original;
+ }
+ }
+ return update;
+ }
+
+ // If no auth on response level we create a response that has all
+ // datasets from the original with the ones that required auth but
+ // not anymore updated and new ones not requiring auth appended.
+
+ // The update shouldn't require auth
+ if (update.getAuthentication() != null) {
+ return original;
+ }
+
+ final FillResponse.Builder builder = new FillResponse.Builder(original.getId());
+
+ // Update existing datasets
+ final ArraySet<Dataset> origDatasets = original.getDatasets();
+ final int origDatasetCount = origDatasets.size();
+ for (int i = 0; i < origDatasetCount; i++) {
+ Dataset origDataset = origDatasets.valueAt(i);
+ ArraySet<Dataset> updateDatasets = update.getDatasets();
+ final int updateDatasetCount = updateDatasets.size();
+ for (int j = 0; j < updateDatasetCount; j++) {
+ Dataset updateDataset = updateDatasets.valueAt(j);
+ if (origDataset.getId().equals(updateDataset.getId())) {
+ // The update shouldn't require auth
+ if (updateDataset.getAuthentication() == null) {
+ origDataset = updateDataset;
+ updateDatasets.removeAt(j);
+ }
+ break;
+ }
+ }
+ builder.addDataset(origDataset);
+ }
+
+ // Add new datasets
+ final ArraySet<Dataset> updateDatasets = update.getDatasets();
+ final int updateDatasetCount = updateDatasets.size();
+ for (int i = 0; i < updateDatasetCount; i++) {
+ final Dataset updateDataset = updateDatasets.valueAt(i);
+ builder.addDataset(updateDataset);
+ }
+
+ // For now no extras and savable id updates.
+
+ return builder.build();
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 76c2916..da54d85 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -18,10 +18,7 @@
import static com.android.server.autofill.Helper.DEBUG;
-import android.annotation.Nullable;
-import android.app.Activity;
import android.app.Notification;
-import android.app.Notification.Action;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -29,12 +26,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.graphics.Rect;
-import android.graphics.PixelFormat;
import android.os.Binder;
-import android.os.Bundle;
+import android.util.ArraySet;
import android.util.Slog;
-import android.view.autofill.AutoFillId;
import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
import android.view.Gravity;
@@ -44,27 +40,28 @@
import android.view.WindowManager.LayoutParams;
import android.widget.Toast;
-import com.android.internal.annotations.GuardedBy;
import com.android.server.UiThread;
-import com.android.server.autofill.AutoFillManagerServiceImpl.Session;
import com.android.server.autofill.AutoFillManagerServiceImpl.ViewState;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.List;
/**
* 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 static final String EXTRA_AUTH_INTENT_SENDER =
+ "com.android.server.autofill.extra.AUTH_INTENT_SENDER";
+ private static final String EXTRA_AUTH_FILL_IN_INTENT =
+ "com.android.server.autofill.extra.AUTH_FILL_IN_INTENT";
+
private final Context mContext;
- private final Session mSession;
private final WindowManager mWm;
+ // TODO(b/33197203) Fix locking - some state requires lock and some not - requires refactoring
+
// Fill UI variables
private AnchoredWindow mFillWindow;
private DatasetPicker mFillView;
@@ -72,21 +69,39 @@
private Rect mBounds;
private String mFilterText;
+ private AutoFillUiCallback mCallback;
+ private int mClientId;
+
+ public interface AutoFillUiCallback {
+ void authenticate(IntentSender intent, Intent fillInIntent);
+ void fill(Dataset dataset);
+ void save();
+ }
+
/**
* Custom snackbar UI used for saving autofill or other informational messages.
*/
private View mSnackbar;
- AutoFillUI(Context context, Session session) {
+ AutoFillUI(Context context) {
mContext = context;
- mSession = session;
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
+ void setCallback(AutoFillUiCallback callback, int clientId) {
+ hideAll();
+ mCallback = callback;
+ mClientId = clientId;
+ }
+
/**
* Displays an error message to the user.
*/
void showError(CharSequence message) {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAll();
// TODO(b/33197203): proper implementation
UiThread.getHandler().runWithScissors(() -> {
Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
@@ -95,21 +110,17 @@
/**
* Hides the fill UI.
- * Shows the options from a {@link FillResponse} so the user can pick up the proper
- * {@link Dataset} (when the response has one) for a given view (identified by
- * {@code autoFillId}).
*/
void hideFillUi() {
UiThread.getHandler().runWithScissors(() -> {
- hideFillUiLocked();
+ hideFillUiUiThread();
}, 0);
}
- // Must be called in inside UI Thread
- private void hideFillUiLocked() {
+ @android.annotation.UiThread
+ private void hideFillUiUiThread() {
if (mFillWindow != null) {
- if (DEBUG) Slog.d(TAG, "hideFillUiLocked(): hide" + mFillWindow);
-
+ if (DEBUG) Slog.d(TAG, "hideFillUiUiThread(): hide" + mFillWindow);
mFillWindow.hide();
}
@@ -120,7 +131,6 @@
mFillWindow = null;
}
-
/**
* Shows the fill UI, removing the previous fill UI if the has changed.
*
@@ -129,26 +139,31 @@
* @param bounds bounds of the view to be filled, used if changed
* @param filterText text of the view to be filled, used if changed
*/
- void showFillUi(ViewState viewState, List<Dataset> datasets, Rect bounds,
+ void showFillUi(ViewState viewState, ArraySet<Dataset> datasets, Rect bounds,
String filterText) {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAll();
UiThread.getHandler().runWithScissors(() -> {
if (mViewState != viewState) {
- // new
- hideFillUi();
-
mViewState = viewState;
mFillView = new DatasetPicker(mContext, datasets,
(dataset) -> {
- mSession.autoFillApp(dataset);
+ final AutoFillUiCallback callback;
+ synchronized (mLock) {
+ callback = mCallback;
+ }
+ callback.fill(dataset);
hideFillUi();
});
+ // TODO: No magical numbers
mFillWindow = new AnchoredWindow(
mWm, mFillView, 800, ViewGroup.LayoutParams.WRAP_CONTENT);
if (DEBUG) Slog.d(TAG, "show FillUi");
}
-
if (!bounds.equals(mBounds)) {
if (DEBUG) Slog.d(TAG, "update FillUi bounds: " + mBounds);
mBounds = bounds;
@@ -170,27 +185,14 @@
* <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to
* autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}.
*/
- void showFillResponseAuthenticationRequest(boolean usesFingerprint,
- Bundle extras, int flags) {
- // TODO(b/33197203): proper implementation
- showAuthNotification(usesFingerprint, extras, flags);
- }
-
- /**
- * Shows an UI affordance asking indicating that user action is required before a
- * {@link Dataset} can be used.
- *
- * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint to
- * autofill".
- */
- void showDatasetFingerprintAuthenticationRequest(Dataset dataset) {
- if (DEBUG) Slog.d(TAG, "showDatasetAuthenticationRequest(): dataset=" + dataset);
-
- // TODO(b/33197203): proper implementation (either pop up a fingerprint dialog or replace
- // the auto-fill bar with a new message.
+ void showFillResponseAuthRequest(IntentSender intent, Intent fillInIntent) {
+ if (!hasCallback()) {
+ return;
+ }
+ hideAll();
UiThread.getHandler().runWithScissors(() -> {
- Toast.makeText(mContext, "AutoFill: press fingerprint to unlock " + dataset.getName(),
- Toast.LENGTH_LONG).show();
+ // TODO(b/33197203): proper implementation
+ showFillResponseAuthUiUiThread(intent, fillInIntent);
}, 0);
}
@@ -198,46 +200,36 @@
* Shows the UI asking the user to save for auto-fill.
*/
void showSaveUi() {
- showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
- @Override
- public void onSaveClick() {
- hideSnackbar();
-
- // TODO(b/33197203): add MetricsLogger call
- mSession.requestSave();
- }
- @Override
- public void onCancelClick() {
- hideSnackbar();
- }
- }));
- }
-
- /**
- * Called by service after the user user the fingerprint sensors to authenticate.
- */
- void dismissFingerprintRequest(boolean success) {
- if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success);
-
- dismissAuthNotification();
-
- if (!success) {
- // TODO(b/33197203): proper implementation (snack bar / i18n string)
- UiThread.getHandler().runWithScissors(() -> {
- Toast.makeText(mContext, "AutoFill: fingerprint failed", Toast.LENGTH_LONG).show();
- }, 0);
+ if (!hasCallback()) {
+ return;
}
+ hideAll();
+ UiThread.getHandler().runWithScissors(() -> {
+ showSnackbarUiThread(new SavePrompt(mContext,
+ new SavePrompt.OnSaveListener() {
+ @Override
+ public void onSaveClick() {
+ hideSnackbarUiThread();
+ // TODO(b/33197203): add MetricsLogger call
+ mCallback.save();
+ }
+
+ @Override
+ public void onCancelClick() {
+ hideSnackbarUiThread();
+ }
+ }));
+ }, 0);
}
/**
- * Closes all UI affordances.
+ * Hides all UI affordances.
*/
- void closeAll() {
- if (DEBUG) Slog.d(TAG, "closeAll()");
-
+ void hideAll() {
UiThread.getHandler().runWithScissors(() -> {
- hideSnackbarLocked();
- hideFillUiLocked();
+ hideSnackbarUiThread();
+ hideFillUiUiThread();
+ hideFillResponseAuthUiUiThread();
}, 0);
}
@@ -245,7 +237,7 @@
pw.println("AufoFill UI");
final String prefix = " ";
pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode);
- pw.print(prefix); pw.print("mSessionId: "); pw.println(mSession.mId);
+ pw.print(prefix); pw.print("mClientId: "); pw.println(mClientId);
pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds);
@@ -254,7 +246,7 @@
//similar to a snackbar, but can be a bit custom since it is more than just text. This will
//allow two buttons for saving or not saving the autofill for instance as well.
- private void showSnackbar(View snackBar) {
+ private void showSnackbarUiThread(View snackBar) {
final LayoutParams params = new LayoutParams();
params.setTitle("AutoFill Save");
params.type = LayoutParams.TYPE_PHONE; // TODO(b/33197203) use app window token
@@ -274,20 +266,19 @@
}, 0);
}
- private void hideSnackbar() {
- UiThread.getHandler().runWithScissors(() -> {
- hideSnackbarLocked();
- }, 0);
- }
-
- // Must be called in inside UI Thread
- private void hideSnackbarLocked() {
+ private void hideSnackbarUiThread() {
if (mSnackbar != null) {
mWm.removeView(mSnackbar);
mSnackbar = null;
}
}
+ private boolean hasCallback() {
+ synchronized (mLock) {
+ return mCallback != null;
+ }
+ }
+
/////////////////////////////////////////////////////////////////////////////////
// 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. //
@@ -297,25 +288,13 @@
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_SESSION_ID = "session_id";
- private static final String EXTRA_FILL_RESPONSE = "fill_response";
- private static final String EXTRA_DATASET = "dataset";
- private static final String EXTRA_AUTH_REQUIRED_EXTRAS = "auth_required_extras";
- private static final String EXTRA_FLAGS = "flags";
-
- private static final String TYPE_OPTIONS = "options";
- private static final String TYPE_AUTH_RESPONSE = "auth_response";
-
private BroadcastReceiver mNotificationReceiver;
private final Object mLock = new Object();
// Hack used to generate unique pending intents
static int sResultCode = 0;
- private void setNotificationListener() {
+ private void ensureNotificationListener() {
synchronized (mLock) {
if (mNotificationReceiver == null) {
mNotificationReceiver = new NotificationReceiver();
@@ -328,83 +307,58 @@
final class NotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
- if (type == null) {
- Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
- return;
- }
- final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
- final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
-
- if (DEBUG) Slog.d(TAG, "Notification received: type=" + type
- + ", sessionId=" + mSession.mId);
+ final AutoFillUiCallback callback;
synchronized (mLock) {
- switch (type) {
- case TYPE_AUTH_RESPONSE:
- mSession.notifyResponseAuthenticationResult(
- intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags);
- break;
- default: {
- Slog.w(TAG, "Unknown notification type: " + type);
- }
- }
+ callback = mCallback;
+ }
+ if (callback != null) {
+ IntentSender intentSender = intent.getParcelableExtra(EXTRA_AUTH_INTENT_SENDER);
+ Intent fillInIntent = intent.getParcelableExtra(EXTRA_AUTH_FILL_IN_INTENT);
+ callback.authenticate(intentSender, fillInIntent);
}
collapseStatusBar();
}
}
- private static Intent newNotificationIntent(String type) {
- final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
- intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
- return intent;
- }
-
- private void showAuthNotification(boolean usesFingerprint,
- Bundle extras, int flags) {
- final long token = Binder.clearCallingIdentity();
- try {
- showAuthNotificationAsSystem(usesFingerprint, extras, flags);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void showAuthNotificationAsSystem(
- boolean usesFingerprint, Bundle extras, int flags) {
+ @android.annotation.UiThread
+ private void showFillResponseAuthUiUiThread(IntentSender intent, Intent fillInIntent) {
final String title = "AutoFill Authentication";
final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
- final Intent authIntent = newNotificationIntent(TYPE_AUTH_RESPONSE);
- if (extras != null) {
- authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras);
- }
- if (flags != 0) {
- authIntent.putExtra(EXTRA_FLAGS, flags);
- }
- final PendingIntent authPendingIntent = PendingIntent.getBroadcast(mContext, ++sResultCode,
- authIntent, PendingIntent.FLAG_ONE_SHOT);
+ final Intent authIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
+ authIntent.putExtra(EXTRA_AUTH_INTENT_SENDER, intent);
+ authIntent.putExtra(EXTRA_AUTH_FILL_IN_INTENT, fillInIntent);
- if (usesFingerprint) {
- subTitle.append("But kindly accepts your fingerprint instead"
- + "\n(tap fingerprint sensor to trigger it)");
+ final PendingIntent authPendingIntent = PendingIntent.getBroadcast(
+ mContext, ++sResultCode, authIntent, PendingIntent.FLAG_ONE_SHOT);
- } else {
- subTitle.append("Tap notification to launch its authentication UI.");
- }
+ subTitle.append("Tap notification to launch its authentication UI.");
final Notification.Builder notification = newNotificationBuilder()
.setAutoCancel(true)
.setOngoing(false)
.setContentTitle(title)
- .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()));
- if (authPendingIntent != null) {
- notification.setContentIntent(authPendingIntent);
+ .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()))
+ .setContentIntent(authPendingIntent);
+
+ ensureNotificationListener();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ NotificationManager.from(mContext).notify(mClientId, notification.build());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- NotificationManager.from(mContext).notify(mSession.mId, notification.build());
}
- private void dismissAuthNotification() {
- NotificationManager.from(mContext).cancel(mSession.mId);
+ @android.annotation.UiThread
+ private void hideFillResponseAuthUiUiThread() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ NotificationManager.from(mContext).cancel(mClientId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private Notification.Builder newNotificationBuilder() {
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
index 7245aaa..8212cf1 100644
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.Color;
+import android.util.ArraySet;
import android.view.autofill.Dataset;
import android.view.View;
import android.view.ViewGroup;
@@ -38,15 +39,13 @@
* 3) the View is detached.
*/
final class DatasetPicker extends ListView implements OnItemClickListener {
- private static final String TAG = "DatasetPicker";
-
interface Listener {
void onDatasetPicked(Dataset dataset);
}
private final Listener mListener;
- DatasetPicker(Context context, List<Dataset> datasets, Listener listener) {
+ DatasetPicker(Context context, ArraySet<Dataset> datasets, Listener listener) {
super(context);
mListener = listener;
@@ -80,7 +79,7 @@
adapter.getFilter().filter(prefix, new FilterListener() {
@Override
public void onFilterComplete(int count) {
- DatasetPicker.this.requestLayout();
+ setVisibility(count > 0 ? View.VISIBLE : View.GONE);
}
});
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 9171dac..7fff410 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -17,6 +17,9 @@
package com.android.server.autofill;
import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.autofill.Dataset;
+import android.view.autofill.FillResponse;
import java.util.Arrays;
import java.util.Objects;
@@ -55,4 +58,26 @@
private Helper() {
throw new UnsupportedOperationException("contains static members only");
}
+
+ /**
+ * Finds a data set by id in a response.
+ *
+ * @param id The dataset id.
+ * @param response The response to search.
+ * @return The dataset if found or null.
+ */
+ static Dataset findDatasetById(String id, FillResponse response) {
+ ArraySet<Dataset> datasets = response.getDatasets();
+ if (datasets == null || datasets.isEmpty()) {
+ return null;
+ }
+ final int datasetCount = datasets.size();
+ for (int i = 0; i < datasetCount; i++) {
+ Dataset dataset = datasets.valueAt(i);
+ if (dataset.getId().equals(id)) {
+ return dataset;
+ }
+ }
+ return null;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
new file mode 100644
index 0000000..c070f77
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.assist.AssistStructure;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.ICancellationSignal;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.IAutoFillService;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.ISaveCallback;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.FillResponse;
+import com.android.internal.os.HandlerCaller;
+import com.android.server.FgThread;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * This class represents a remote fill service. It abstracts away the binding
+ * and unbinding from the remote implementation.
+ *
+ * <p>Clients can call methods of this class without worrying about when and
+ * how to bind/unbind/timeout. All state of this class is modified on a handler
+ * thread.
+ */
+final class RemoteFillService implements DeathRecipient {
+ private static final String LOG_TAG = "RemoteFillService";
+
+ private static final boolean DEBUG = Helper.DEBUG;
+
+ // How long after the last interaction with the service we would unbind
+ private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.MINUTE_IN_MILLIS;
+
+ private final Context mContext;
+
+ private final ComponentName mComponentName;
+
+ private final Intent mIntent;
+
+ private final FillServiceCallbacks mCallbacks;
+
+ private final int mUserId;
+
+ private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
+
+ private final HandlerCaller mHandler;
+
+ private IAutoFillService mAutoFillService;
+
+ private boolean mBinding;
+
+ private boolean mDestroyed;
+
+ private boolean mServiceDied;
+
+ private boolean mCompleted;
+
+ private PendingRequest mPendingRequest;
+
+ public interface FillServiceCallbacks {
+ void onFillRequestSuccess(FillResponse response);
+ void onFillRequestFailure(CharSequence message);
+ void onSaveRequestSuccess();
+ void onSaveRequestFailure(CharSequence message);
+ void onServiceDied(RemoteFillService service);
+ }
+
+ public RemoteFillService(Context context, ComponentName componentName,
+ int userId, FillServiceCallbacks callbacks) {
+ mContext = context;
+ mCallbacks = callbacks;
+ mComponentName = componentName;
+ mIntent = new Intent(AutoFillService.SERVICE_INTERFACE)
+ .setComponent(mComponentName);
+ mUserId = userId;
+ mHandler = new MyHandler(context);
+ }
+
+ public void destroy() {
+ mHandler.obtainMessage(MyHandler.MSG_DESTROY).sendToTarget();
+ }
+
+ private void handleDestroy() {
+ if (mPendingRequest != null) {
+ mPendingRequest.cancel();
+ mPendingRequest = null;
+ }
+ ensureUnbound();
+ mDestroyed = true;
+ }
+
+ @Override
+ public void binderDied() {
+ mHandler.obtainMessage(MyHandler.MSG_BINDER_DIED).sendToTarget();
+ }
+
+ private void handleBinderDied() {
+ if (mAutoFillService != null) {
+ mAutoFillService.asBinder().unlinkToDeath(this, 0);
+ }
+ mAutoFillService = null;
+ mServiceDied = true;
+ mCallbacks.onServiceDied(this);
+ }
+
+ public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+ cancelScheduledUnbind();
+ PendingFillRequest request = new PendingFillRequest(structure, extras, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ }
+
+ public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+ cancelScheduledUnbind();
+ PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ }
+
+ // Note: we are dumping without a lock held so this is a bit racy but
+ // adding a lock to a class that offloads to a handler thread would
+ // mean adding a lock adding overhead to normal runtime operation.
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ String tab = " ";
+ pw.append(prefix).append("service:").println();
+ pw.append(prefix).append(tab).append("userId=")
+ .append(String.valueOf(mUserId)).println();
+ pw.append(prefix).append(tab).append("componentName=")
+ .append(mComponentName.flattenToString()).println();
+ pw.append(prefix).append(tab).append("destroyed=")
+ .append(String.valueOf(mDestroyed)).println();
+ pw.append(prefix).append(tab).append("bound=")
+ .append(String.valueOf(isBound())).println();
+ pw.append(prefix).append(tab).append("hasPendingRequest=")
+ .append(String.valueOf(mPendingRequest != null)).println();
+ pw.println();
+ }
+
+ private void cancelScheduledUnbind() {
+ mHandler.removeMessages(MyHandler.MSG_UNBIND);
+ }
+
+ private void scheduleUnbind() {
+ cancelScheduledUnbind();
+ Message message = mHandler.obtainMessage(MyHandler.MSG_UNBIND);
+ mHandler.sendMessageDelayed(message, TIMEOUT_IDLE_BIND_MILLIS);
+ }
+
+ private void handleUnbind() {
+ ensureUnbound();
+ }
+
+ private void handlePendingRequest(PendingRequest pendingRequest) {
+ if (mDestroyed || mCompleted) {
+ return;
+ }
+ if (pendingRequest.isFinal()) {
+ mCompleted = true;
+ }
+ if (!isBound()) {
+ if (mPendingRequest != null) {
+ mPendingRequest.cancel();
+ }
+ mPendingRequest = pendingRequest;
+ ensureBound();
+ } else {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "[user: " + mUserId + "] handleOnFillRequest()");
+ }
+ pendingRequest.run();
+ }
+ }
+
+ private boolean isBound() {
+ return mAutoFillService != null;
+ }
+
+ private void ensureBound() {
+ if (isBound() || mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
+ }
+ mBinding = true;
+
+ boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(mUserId));
+
+ if (!willBind) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent);
+ }
+ mBinding = false;
+
+ if (!mServiceDied) {
+ handleBinderDied();
+ }
+ }
+ }
+
+ private void ensureUnbound() {
+ if (!isBound() && !mBinding) {
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
+ }
+ mBinding = false;
+ if (isBound()) {
+ mAutoFillService.asBinder().unlinkToDeath(this, 0);
+ mAutoFillService = null;
+ }
+ mContext.unbindService(mServiceConnection);
+ }
+
+ private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
+ FillResponse response) {
+ mHandler.getHandler().post(() -> {
+ if (handleResponseCallbackCommon(pendingRequest)) {
+ mCallbacks.onFillRequestSuccess(response);
+ }
+ });
+ }
+
+ private void dispatchOnFillRequestFailure(PendingRequest pendingRequest,
+ CharSequence message) {
+ mHandler.getHandler().post(() -> {
+ if (handleResponseCallbackCommon(pendingRequest)) {
+ mCallbacks.onFillRequestFailure(message);
+ }
+ });
+ }
+
+ private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest) {
+ mHandler.getHandler().post(() -> {
+ if (handleResponseCallbackCommon(pendingRequest)) {
+ mCallbacks.onSaveRequestSuccess();
+ }
+ });
+ }
+
+ private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
+ CharSequence message) {
+ mHandler.getHandler().post(() -> {
+ if (handleResponseCallbackCommon(pendingRequest)) {
+ mCallbacks.onSaveRequestFailure(message);
+ }
+ });
+ }
+
+ private boolean handleResponseCallbackCommon(PendingRequest pendingRequest) {
+ if (mDestroyed) {
+ return false;
+ }
+ if (mPendingRequest == pendingRequest) {
+ mPendingRequest = null;
+ }
+ if (mPendingRequest == null) {
+ scheduleUnbind();
+ }
+ return true;
+ }
+
+ private class RemoteServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mDestroyed || !mBinding) {
+ mContext.unbindService(mServiceConnection);
+ return;
+ }
+ mBinding = false;
+ mAutoFillService = IAutoFillService.Stub.asInterface(service);
+ try {
+ service.linkToDeath(RemoteFillService.this, 0);
+ } catch (RemoteException re) {
+ handleBinderDied();
+ return;
+ }
+
+ if (mPendingRequest != null) {
+ handlePendingRequest(mPendingRequest);
+ }
+
+ mServiceDied = false;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinding = true;
+ mAutoFillService = null;
+ }
+ }
+
+ private final class MyHandler extends HandlerCaller {
+ public static final int MSG_DESTROY = 1;
+ public static final int MSG_BINDER_DIED = 2;
+ public static final int MSG_UNBIND = 3;
+ public static final int MSG_ON_PENDING_REQUEST = 4;
+
+ public MyHandler(Context context) {
+ // Cannot use lambda - doesn't compile
+ super(context, FgThread.getHandler().getLooper(), new Callback() {
+ @Override
+ public void executeMessage(Message message) {
+ if (mDestroyed) {
+ Slog.w(LOG_TAG, "Not handling " + message + " as service for "
+ + mComponentName + " is already destroyed");
+ return;
+ }
+ switch (message.what) {
+ case MSG_DESTROY: {
+ handleDestroy();
+ } break;
+
+ case MSG_BINDER_DIED: {
+ handleBinderDied();
+ } break;
+
+ case MSG_UNBIND: {
+ handleUnbind();
+ } break;
+
+ case MSG_ON_PENDING_REQUEST: {
+ handlePendingRequest((PendingRequest) message.obj);
+ } break;
+ }
+ }
+ }, false);
+ }
+ }
+
+ private static abstract class PendingRequest implements Runnable {
+ void cancel() {
+
+ }
+
+ /**
+ * @return whether this request leads to a final state where no
+ * other requests can be made.
+ */
+ boolean isFinal() {
+ return false;
+ }
+ }
+
+ private static final class PendingFillRequest extends PendingRequest {
+ private final Object mLock = new Object();
+ private final WeakReference<RemoteFillService> mWeakService;
+ private AssistStructure mStructure;
+ private Bundle mExtras;
+ private final IFillCallback mCallback;
+ private ICancellationSignal mCancellation;
+ private boolean mCancelled;
+
+ public PendingFillRequest(AssistStructure structure,
+ Bundle extras, RemoteFillService service) {
+ mStructure = structure;
+ mExtras = extras;
+ mWeakService = new WeakReference<>(service);
+ mCallback = new IFillCallback.Stub() {
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ synchronized (mLock) {
+ final boolean cancelled;
+ synchronized (mLock) {
+ mCancellation = cancellation;
+ cancelled = mCancelled;
+ }
+ if (cancelled) {
+ try {
+ cancellation.cancel();
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error requesting a cancellation", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ RemoteFillService remoteService = mWeakService.get();
+ if (remoteService != null) {
+ remoteService.dispatchOnFillRequestSuccess(
+ PendingFillRequest.this, response);
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence message) {
+ RemoteFillService remoteService = mWeakService.get();
+ if (remoteService != null) {
+ remoteService.dispatchOnFillRequestFailure(
+ PendingFillRequest.this, message);
+ }
+ }
+ };
+ }
+
+ @Override
+ public void run() {
+ RemoteFillService remoteService = mWeakService.get();
+ if (remoteService != null) {
+ try {
+ remoteService.mAutoFillService.onFillRequest(mStructure,
+ mExtras, mCallback);
+ synchronized (mLock) {
+ mStructure = null;
+ mExtras = null;
+ }
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error calling on fill request", e);
+ cancel();
+ }
+ }
+ }
+
+ @Override
+ public void cancel() {
+ final ICancellationSignal cancellation;
+ synchronized (mLock) {
+ if (mCancelled) {
+ return;
+ }
+ mCancelled = true;
+ cancellation = mCancellation;
+ }
+ if (cancellation == null) {
+ return;
+ }
+ try {
+ cancellation.cancel();
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error cancelling a fill request", e);
+ }
+ }
+ }
+
+ private static final class PendingSaveRequest extends PendingRequest {
+ private final Object mLock = new Object();
+ private final WeakReference<RemoteFillService> mWeakService;
+ private AssistStructure mStructure;
+ private Bundle mExtras;
+ private final ISaveCallback mCallback;
+
+ public PendingSaveRequest(@NonNull AssistStructure structure,
+ @Nullable Bundle extras, @NonNull RemoteFillService service) {
+ mStructure = structure;
+ mExtras = extras;
+ mWeakService = new WeakReference<>(service);
+ mCallback = new ISaveCallback.Stub() {
+ @Override
+ public void onSuccess() {
+ RemoteFillService service = mWeakService.get();
+ if (service != null) {
+ service.dispatchOnSaveRequestSuccess(
+ PendingSaveRequest.this);
+ }
+ }
+
+ @Override
+ public void onFailure(CharSequence message) {
+ RemoteFillService service = mWeakService.get();
+ if (service != null) {
+ service.dispatchOnSaveRequestFailure(
+ PendingSaveRequest.this, message);
+ }
+ }
+ };
+ }
+
+ @Override
+ public void run() {
+ RemoteFillService service = mWeakService.get();
+ if (service != null) {
+ try {
+ service.mAutoFillService.onSaveRequest(mStructure,
+ mExtras, mCallback);
+ synchronized (mLock) {
+ mStructure = null;
+ mExtras = null;
+ }
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Error calling on save request", e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isFinal() {
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index 1314110..f5fe3db 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -51,6 +51,7 @@
private static final int MSG_REGISTER_TRACKER = 2;
private static final int MSG_UNREGISTER_TRACKER = 3;
private static final int MSG_REMOVE_USER = 4;
+ private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
"LockSettingsStrongAuth.timeoutForUser";
@@ -128,6 +129,23 @@
}
}
+ private void handleScheduleStrongAuthTimeout(int userId) {
+ final DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null, userId);
+ // cancel current alarm listener for the user (if there was one)
+ StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
+ if (alarm != null) {
+ mAlarmManager.cancel(alarm);
+ } else {
+ alarm = new StrongAuthTimeoutAlarmListener(userId);
+ mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm);
+ }
+ // schedule a new alarm listener for the user
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
+ alarm, mHandler);
+ }
+
private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
try {
@@ -151,7 +169,8 @@
}
public void removeUser(int userId) {
- mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
+ final int argNotUsed = 0;
+ mHandler.obtainMessage(MSG_REMOVE_USER, userId, argNotUsed).sendToTarget();
}
public void requireStrongAuth(int strongAuthReason, int userId) {
@@ -169,29 +188,8 @@
}
public void reportSuccessfulStrongAuthUnlock(int userId) {
- scheduleStrongAuthTimeout(userId);
- }
-
- private void scheduleStrongAuthTimeout(int userId) {
- final DevicePolicyManager dpm =
- (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null, userId);
- // cancel current alarm listener for the user (if there was one)
- StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
- if (alarm != null) {
- mAlarmManager.cancel(alarm);
- } else {
- alarm = new StrongAuthTimeoutAlarmListener(userId);
- mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm);
- }
- // schedule a new alarm listener for the user
- final long ident = Binder.clearCallingIdentity();
- try {
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
- alarm, mHandler);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ final int argNotUsed = 0;
+ mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
}
private class StrongAuthTimeoutAlarmListener implements OnAlarmListener {
@@ -224,6 +222,9 @@
case MSG_REMOVE_USER:
handleRemoveUser(msg.arg1);
break;
+ case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT:
+ handleScheduleStrongAuthTimeout(msg.arg1);
+ break;
}
}
};
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 22dbf44..5fe8b1a 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -927,7 +927,7 @@
try {
for (int i = 0; i < count; i++) {
consumer.accept(callbackList.getBroadcastItem(i),
- callbackList.getRegisteredCallbackCookie(i));
+ callbackList.getBroadcastCookie(i));
}
} finally {
callbackList.finishBroadcast();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5655dc5..041fee2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import java.io.FileDescriptor;
@@ -1391,8 +1393,7 @@
}
if (r != null) {
if (mAm.checkComponentPermission(r.permission,
- callingPid, callingUid, r.appInfo.uid, r.exported)
- != PackageManager.PERMISSION_GRANTED) {
+ callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
if (!r.exported) {
Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ " from pid=" + callingPid
@@ -2775,16 +2776,15 @@
return info;
}
- List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, int flags) {
+ List<ActivityManager.RunningServiceInfo> getRunningServiceInfoLocked(int maxNum, int flags,
+ int callingUid, boolean allowed) {
ArrayList<ActivityManager.RunningServiceInfo> res
= new ArrayList<ActivityManager.RunningServiceInfo>();
- final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- if (ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- uid) == PackageManager.PERMISSION_GRANTED) {
+ if (ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid)
+ == PERMISSION_GRANTED) {
int[] users = mAm.mUserController.getUsers();
for (int ui=0; ui<users.length && res.size() < maxNum; ui++) {
ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(users[ui]);
@@ -2802,16 +2802,20 @@
res.add(info);
}
} else {
- int userId = UserHandle.getUserId(uid);
+ int userId = UserHandle.getUserId(callingUid);
ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(userId);
for (int i=0; i<alls.size() && res.size() < maxNum; i++) {
ServiceRecord sr = alls.valueAt(i);
- res.add(makeRunningServiceInfoLocked(sr));
+
+ if (allowed || (sr.app != null && sr.app.uid == callingUid)) {
+ res.add(makeRunningServiceInfoLocked(sr));
+ }
}
for (int i=0; i<mRestartingServices.size() && res.size() < maxNum; i++) {
ServiceRecord r = mRestartingServices.get(i);
- if (r.userId == userId) {
+ if (r.userId == userId
+ && (allowed || (r.app != null && r.app.uid == callingUid))) {
ActivityManager.RunningServiceInfo info =
makeRunningServiceInfoLocked(r);
info.restarting = r.nextRestartTime;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5ed8290..d75048d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17467,7 +17467,11 @@
int flags) {
enforceNotIsolatedCaller("getServices");
synchronized (this) {
- return mServices.getRunningServiceInfoLocked(maxNum, flags);
+ final int callingUid = Binder.getCallingUid();
+ final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(),
+ callingUid);
+
+ return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid, allowed);
}
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 3da49d8..168744b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -18,9 +18,6 @@
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
-import com.android.server.twilight.TwilightListener;
-import com.android.server.twilight.TwilightManager;
-import com.android.server.twilight.TwilightState;
import android.annotation.Nullable;
import android.hardware.Sensor;
@@ -55,9 +52,6 @@
// non-zero, which in turn ensures that the total weight is non-zero.
private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
- // Specifies the maximum magnitude of the time of day adjustment.
- private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f;
-
// Debounce for sampling user-initiated changes in display brightness to ensure
// the user is satisfied with the result before storing the sample.
private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
@@ -74,9 +68,6 @@
// The light sensor, or null if not available or needed.
private final Sensor mLightSensor;
- // The twilight service.
- private final TwilightManager mTwilight;
-
// The auto-brightness spline adjustment.
// The brightness values have been scaled to a range of 0..1.
private final Spline mScreenAutoBrightnessSpline;
@@ -186,8 +177,6 @@
private int mBrightnessAdjustmentSampleOldBrightness;
private float mBrightnessAdjustmentSampleOldGamma;
- private boolean mUseTwilight;
-
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -196,7 +185,6 @@
int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma,
HysteresisLevels dynamicHysteresis) {
mCallbacks = callbacks;
- mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
mScreenAutoBrightnessSpline = autoBrightnessSpline;
mScreenBrightnessRangeMinimum = brightnessMin;
@@ -233,7 +221,7 @@
}
public void configure(boolean enable, float adjustment, boolean dozing,
- boolean userInitiatedChange, boolean useTwilight) {
+ boolean userInitiatedChange) {
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
@@ -242,7 +230,6 @@
mDozing = dozing;
boolean changed = setLightSensorEnabled(enable && !dozing);
changed |= setScreenAutoBrightnessAdjustment(adjustment);
- changed |= setUseTwilight(useTwilight);
if (changed) {
updateAutoBrightness(false /*sendUpdate*/);
}
@@ -251,17 +238,6 @@
}
}
- private boolean setUseTwilight(boolean useTwilight) {
- if (mUseTwilight == useTwilight) return false;
- if (useTwilight) {
- mTwilight.registerListener(mTwilightListener, mHandler);
- } else {
- mTwilight.unregisterListener(mTwilightListener);
- }
- mUseTwilight = useTwilight;
- return true;
- }
-
public void dump(PrintWriter pw) {
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
@@ -276,7 +252,6 @@
pw.println();
pw.println("Automatic Brightness Controller State:");
pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mTwilight.getLastTwilightState()=" + mTwilight.getLastTwilightState());
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
pw.println(" mAmbientLux=" + mAmbientLux);
@@ -522,19 +497,6 @@
}
}
- if (mUseTwilight) {
- TwilightState state = mTwilight.getLastTwilightState();
- if (state != null && state.isNight()) {
- final long duration = state.sunriseTimeMillis() - state.sunsetTimeMillis();
- final long progress = System.currentTimeMillis() - state.sunsetTimeMillis();
- final float amount = (float) Math.pow(2.0 * progress / duration - 1.0, 2.0);
- gamma *= 1 + amount * TWILIGHT_ADJUSTMENT_MAX_GAMMA;
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: twilight amount=" + amount);
- }
- }
- }
-
if (gamma != 1.0f) {
final float in = value;
value = MathUtils.pow(value, gamma);
@@ -649,13 +611,6 @@
}
};
- private final TwilightListener mTwilightListener = new TwilightListener() {
- @Override
- public void onTwilightStateChanged(@Nullable TwilightState state) {
- updateAutoBrightness(true /*sendUpdate*/);
- }
- };
-
/** Callbacks to request updates to the display's power state. */
interface Callbacks {
void updateBrightness();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 015345c..bed269c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -642,7 +642,7 @@
&& mPowerRequest.brightnessSetByUser;
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON,
- userInitiatedChange, mPowerRequest.useTwilight);
+ userInitiatedChange);
}
// Apply brightness boost.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ede6e30..10ecb86 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1123,6 +1123,38 @@
}
}
+ public void addQueueItem(MediaDescription description) {
+ try {
+ mCb.onAddQueueItem(description);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in addQueueItem.", e);
+ }
+ }
+
+ public void addQueueItemAt(MediaDescription description, int index) {
+ try {
+ mCb.onAddQueueItemAt(description, index);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in addQueueItemAt.", e);
+ }
+ }
+
+ public void removeQueueItem(MediaDescription description) {
+ try {
+ mCb.onRemoveQueueItem(description);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in removeQueueItem.", e);
+ }
+ }
+
+ public void removeQueueItemAt(int index) {
+ try {
+ mCb.onRemoveQueueItemAt(index);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in removeQueueItem.", e);
+ }
+ }
+
public void adjustVolume(int direction) {
try {
mCb.onAdjustVolume(direction);
@@ -1397,6 +1429,30 @@
}
@Override
+ public void addQueueItem(MediaDescription description) {
+ updateCallingPackage();
+ mSessionCb.addQueueItem(description);
+ }
+
+ @Override
+ public void addQueueItemAt(MediaDescription description, int index) {
+ updateCallingPackage();
+ mSessionCb.addQueueItemAt(description, index);
+ }
+
+ @Override
+ public void removeQueueItem(MediaDescription description) {
+ updateCallingPackage();
+ mSessionCb.removeQueueItem(description);
+ }
+
+ @Override
+ public void removeQueueItemAt(int index) {
+ updateCallingPackage();
+ mSessionCb.removeQueueItemAt(index);
+ }
+
+ @Override
public CharSequence getQueueTitle() {
return mQueueTitle;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3bf95ef..98177fe 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -155,10 +155,10 @@
}
/**
- * Tells the system UI that volume has changed on a remote session.
+ * Tells the system UI that volume has changed on an active remote session.
*/
public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
- if (mRvc == null) {
+ if (mRvc == null || !session.isActive()) {
return;
}
try {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 279bf90..d6c89a4 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -159,8 +159,7 @@
}
private boolean isMediaNotification(NotificationRecord record) {
- return record.getNotification().extras.getParcelable(
- Notification.EXTRA_MEDIA_SESSION) != null;
+ return record.getNotification().hasMediaSession();
}
private boolean isCall(NotificationRecord record) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0e5ea6e..072959f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2161,19 +2161,24 @@
if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
// We will wait for up to 100 seconds.
- final long timeEnd = SystemClock.uptimeMillis() + 100 * 1000;
+ final long timeStart = SystemClock.uptimeMillis();
+ final long timeEnd = timeStart + 100 * 1000;
+ long timeNow = timeStart;
while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
try {
Thread.sleep(WAIT_TIME_MS);
} catch (InterruptedException e) {
// Do nothing
}
- if (SystemClock.uptimeMillis() > timeEnd) {
+ timeNow = SystemClock.uptimeMillis();
+ if (timeNow > timeEnd) {
SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
Slog.wtf(TAG, "cppreopt did not finish!");
break;
}
}
+
+ Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms");
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 238866a..91a5f4f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -517,9 +517,6 @@
private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
= new ArrayList<PowerManagerInternal.LowPowerModeListener>();
- // True if brightness should be affected by twilight.
- private boolean mBrightnessUseTwilight;
-
// True if we are currently in VR Mode.
private boolean mIsVrModeEnabled;
@@ -735,9 +732,6 @@
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DOUBLE_TAP_TO_WAKE),
false, mSettingsObserver, UserHandle.USER_ALL);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Secure.BRIGHTNESS_USE_TWILIGHT),
- false, mSettingsObserver, UserHandle.USER_ALL);
IVrManager vrManager =
(IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
if (vrManager != null) {
@@ -878,9 +872,6 @@
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
- mBrightnessUseTwilight = Settings.Secure.getIntForUser(resolver,
- Secure.BRIGHTNESS_USE_TWILIGHT, 0, UserHandle.USER_CURRENT) != 0;
-
final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE, 0) != 0;
final boolean autoLowPowerModeConfigured = Settings.Global.getInt(resolver,
@@ -2254,7 +2245,6 @@
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
- mDisplayPowerRequest.useTwilight = mBrightnessUseTwilight;
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index fb0dd2a..b4467af 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -23,9 +23,6 @@
public interface StatusBarManagerInternal {
void setNotificationDelegate(NotificationDelegate delegate);
- void buzzBeepBlinked();
- void notificationLightPulse(int argb, int onMillis, int offMillis);
- void notificationLightOff();
void showScreenPinningRequest(int taskId);
void showAssistDisclosure();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7b7db0e..2dfe20a8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -120,40 +120,6 @@
}
@Override
- public void buzzBeepBlinked() {
- if (mBar != null) {
- try {
- mBar.buzzBeepBlinked();
- } catch (RemoteException ex) {
- }
- }
- }
-
- @Override
- public void notificationLightPulse(int argb, int onMillis, int offMillis) {
- mNotificationLightOn = true;
- if (mBar != null) {
- try {
- mBar.notificationLightPulse(argb, onMillis, offMillis);
- } catch (RemoteException ex) {
- }
- }
- }
-
- @Override
- public void notificationLightOff() {
- if (mNotificationLightOn) {
- mNotificationLightOn = false;
- if (mBar != null) {
- try {
- mBar.notificationLightOff();
- } catch (RemoteException ex) {
- }
- }
- }
- }
-
- @Override
public void showScreenPinningRequest(int taskId) {
if (mBar != null) {
try {
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 0fc1900..45b7baf 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -79,4 +79,13 @@
* given in {@link android.service.vr.VrModeException} on failure.
*/
public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
+
+ /**
+ * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
+ * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used
+ * by VR viewers to indicate that a device is placed in a VR viewer.
+ *
+ * @param enabled true if the device should be placed in persistent VR mode.
+ */
+ public abstract void setPersistentVrModeEnabled(boolean enabled);
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 8368b05..51c4ce31 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -20,15 +20,10 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.annotation.NonNull;
-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.FeatureInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
@@ -53,7 +48,6 @@
import android.util.SparseArray;
import com.android.internal.R;
-import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.utils.ManagedApplicationService.PendingEvent;
@@ -69,8 +63,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import java.util.List;
-import java.util.Map;
import java.util.Objects;
/**
@@ -121,8 +113,10 @@
// State protected by mLock
private boolean mVrModeAllowed;
private boolean mVrModeEnabled;
+ private boolean mPersistentVrModeEnabled;
private EnabledComponentsObserver mComponentObserver;
private ManagedApplicationService mCurrentVrService;
+ private ComponentName mDefaultVrService;
private Context mContext;
private ComponentName mCurrentVrModeComponent;
private int mCurrentVrModeUser;
@@ -157,6 +151,10 @@
if (mVrModeAllowed) {
consumeAndApplyPendingStateLocked();
} else {
+ // Disable persistent mode when VR mode isn't allowed, allows an escape hatch to
+ // exit persistent VR mode when screen is turned off.
+ mPersistentVrModeEnabled = false;
+
// Set pending state to current state.
mPendingState = (mVrModeEnabled && mCurrentVrService != null)
? new VrState(mVrModeEnabled, mCurrentVrService.getComponent(),
@@ -378,6 +376,12 @@
}
@Override
+ public void setPersistentVrModeEnabled(boolean enabled) {
+ enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+ VrManagerService.this.setPersistentVrModeEnabled(enabled);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -387,6 +391,8 @@
}
pw.println("********* Dump of VrManagerService *********");
pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
+ pw.println("Persistent VR mode is currently: " +
+ ((mPersistentVrModeEnabled) ? "enabled" : "disabled"));
pw.println("Previous state transitions:\n");
String tab = " ";
dumpStateTransitions(pw);
@@ -462,6 +468,11 @@
public int hasVrPackage(ComponentName packageName, int userId) {
return VrManagerService.this.hasVrPackage(packageName, userId);
}
+
+ @Override
+ public void setPersistentVrModeEnabled(boolean enabled) {
+ VrManagerService.this.setPersistentVrModeEnabled(enabled);
+ }
}
public VrManagerService(Context context) {
@@ -494,6 +505,15 @@
mComponentObserver.rebuildAll();
}
+
+ //TODO: something more robust than picking the first one
+ ArraySet<ComponentName> defaultVrComponents =
+ SystemConfig.getInstance().getDefaultVrComponents();
+ if (defaultVrComponents.size() > 0) {
+ mDefaultVrService = defaultVrComponents.valueAt(0);
+ } else {
+ Slog.i(TAG, "No default vr listener service found.");
+ }
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
mVrModeAllowed = true;
@@ -637,8 +657,8 @@
}
}
+ mCurrentVrModeComponent = calling;
if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {
- mCurrentVrModeComponent = calling;
sendUpdatedCaller = true;
}
@@ -947,7 +967,25 @@
int userId, @NonNull ComponentName callingPackage) {
synchronized (mLock) {
- VrState pending = new VrState(enabled, targetPackageName, userId, callingPackage);
+ VrState pending;
+ ComponentName targetListener;
+ ComponentName foregroundVrComponent;
+
+ // If the device is in persistent VR mode, then calls to disable VR mode are ignored,
+ // and the system default VR listener is used.
+ boolean targetEnabledState = enabled || mPersistentVrModeEnabled;
+ if (!enabled && mPersistentVrModeEnabled) {
+ targetListener = mDefaultVrService;
+
+ // Current foreground component isn't a VR one (in 2D app case)
+ foregroundVrComponent = null;
+ } else {
+ targetListener = targetPackageName;
+ foregroundVrComponent = callingPackage;
+ }
+ pending = new VrState(
+ targetEnabledState, targetListener, userId, foregroundVrComponent);
+
if (!mVrModeAllowed) {
// We're not allowed to be in VR mode. Make this state pending. This will be
// applied the next time we are allowed to enter VR mode unless it is superseded by
@@ -956,7 +994,7 @@
return;
}
- if (!enabled && mCurrentVrService != null) {
+ if (!targetEnabledState && mCurrentVrService != null) {
// If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
// and service bind/unbind in case we are immediately switching to another VR app.
if (mPendingState == null) {
@@ -971,7 +1009,19 @@
mPendingState = null;
}
- updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
+ updateCurrentVrServiceLocked(
+ targetEnabledState, targetListener, userId, foregroundVrComponent);
+ }
+ }
+
+ private void setPersistentVrModeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mPersistentVrModeEnabled = enabled;
+
+ // Disabling persistent mode when not showing a VR should disable the overall vr mode.
+ if (!enabled && mCurrentVrModeComponent == null) {
+ setVrMode(false, null, 0, null);
+ }
}
}
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index 545b3d7..703518d 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -70,7 +70,7 @@
static void nativeInit(JNIEnv* env, jobject obj) {
// TODO(b/31632518)
if (gThermalModule == nullptr) {
- gThermalModule = IThermal::getService("thermal");
+ gThermalModule = IThermal::getService();
}
if (gThermalModule == nullptr) {
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index e46490b..3120af56 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -26,18 +26,11 @@
namespace android {
-static int start_sensor_service(void* /*unused*/) {
- SensorService::instantiate();
- return 0;
-}
-
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
- // Start the sensor service in a new thread
- createThreadEtc(start_sensor_service, nullptr,
- "StartSensorThread", PRIORITY_FOREGROUND);
+ SensorService::instantiate();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 578e0b3..deb9ee0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6706,6 +6706,8 @@
policy.mDelegationMap.clear();
policy.mStatusBarDisabled = false;
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
+ policy.mAffiliationIds.clear();
+ policy.mLockTaskPackages.clear();
saveSettingsLocked(userId);
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b911d2d..3eebd89 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -125,8 +125,13 @@
public final class SystemServer {
private static final String TAG = "SystemServer";
+ // Tag for timing measurement of main thread.
+ private static final String SYSTEM_SERVER_TIMING_TAG = "SystemServerTiming";
+ // Tag for timing measurement of non-main asynchronous operations.
+ private static final String SYSTEM_SERVER_TIMING_ASYNC_TAG = SYSTEM_SERVER_TIMING_TAG + "Async";
+
private static final BootTimingsTraceLog BOOT_TIMINGS_TRACE_LOG
- = new BootTimingsTraceLog("SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
+ = new BootTimingsTraceLog(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
private static final String ENCRYPTED_STATE = "1";
@@ -227,8 +232,11 @@
private boolean mFirstBoot;
private final boolean mRuntimeRestart;
+ private static final String START_SENSOR_SERVICE = "StartSensorService";
+ private Future<?> mSensorServiceStart;
+
/**
- * Start the sensor service.
+ * Start the sensor service. This is a blocking call and can take time.
*/
private static native void startSensorService();
@@ -382,7 +390,7 @@
MetricsLogger.histogram(null, "boot_system_server_ready", uptimeMillis);
final int MAX_UPTIME_MILLIS = 60 * 1000;
if (uptimeMillis > MAX_UPTIME_MILLIS) {
- Slog.wtf("SystemServerTiming",
+ Slog.wtf(SYSTEM_SERVER_TIMING_TAG,
"SystemServer init took too long. uptimeMillis=" + uptimeMillis);
}
}
@@ -584,9 +592,15 @@
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
- traceBeginAndSlog("StartSensorService");
- startSensorService();
- traceEnd();
+ // Start sensor service in a separate thread. Completion should be checked
+ // before using it.
+ mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {
+ BootTimingsTraceLog traceLog = new BootTimingsTraceLog(
+ SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+ traceLog.traceBegin(START_SENSOR_SERVICE);
+ startSensorService();
+ traceLog.traceEnd();
+ }, START_SENSOR_SERVICE);
}
/**
@@ -742,6 +756,9 @@
traceEnd();
traceBeginAndSlog("StartWindowManagerService");
+ // WMS needs sensor service ready
+ ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
+ mSensorServiceStart = null;
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
@@ -1590,7 +1607,7 @@
webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
BootTimingsTraceLog traceLog = new BootTimingsTraceLog(
- "SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
+ SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
traceLog.traceBegin(WEBVIEW_PREPARATION);
mWebViewUpdateService.prepareWebViewInSystemServer();
traceLog.traceEnd();
@@ -1651,13 +1668,13 @@
Watchdog.getInstance().start();
traceEnd();
- if (webviewPrep != null) {
- ConcurrentUtils.waitForFutureNoInterrupt(webviewPrep, WEBVIEW_PREPARATION);
- }
-
// It is now okay to let the various system services start their
// third party code...
traceBeginAndSlog("PhaseThirdPartyAppsCanStart");
+ // confirm webview completion before starting 3rd party
+ if (webviewPrep != null) {
+ ConcurrentUtils.waitForFutureNoInterrupt(webviewPrep, WEBVIEW_PREPARATION);
+ }
mSystemServiceManager.startBootPhase(
SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
traceEnd();
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index f30c466..f943ee2c 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -541,6 +541,8 @@
}
private void stopDemoMode() {
+ mDeviceInDemoMode = false;
+ mIsCarrierDemoMode = false;
mPreloadAppsInstaller = null;
mCameraIdsWithFlash = null;
mInjector.destroyWakeLock();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7df638c..6fb65d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2089,9 +2089,19 @@
assertTrue(dpm.getAffiliationIds(admin2).isEmpty());
assertFalse(dpm.isAffiliatedUser());
+ // Set affiliation ids again, then clear PO to check that the user becomes unaffiliated
+ dpm.setAffiliationIds(admin2, userAffiliationIds);
+ assertTrue(dpm.isAffiliatedUser());
+ dpm.clearProfileOwner(admin2);
+ assertFalse(dpm.isAffiliatedUser());
+
// Check that the system user remains affiliated.
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
assertTrue(dpm.isAffiliatedUser());
+
+ // Clear the device owner - the user becomes unaffiliated.
+ clearDeviceOwner();
+ assertFalse(dpm.isAffiliatedUser());
}
public void testGetUserProvisioningState_defaultResult() {
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
index 6629770..251bf24 100644
--- a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
@@ -30,8 +30,9 @@
import android.transition.AutoTransition;
import android.transition.ChangeBounds;
import android.transition.Transition;
-import android.transition.TransitionSet;
+import android.transition.TransitionListenerAdapter;
import android.transition.TransitionManager;
+import android.transition.TransitionSet;
import java.util.ArrayList;
import java.util.HashMap;
@@ -84,7 +85,7 @@
fadeIn.setDuration(50);
noFadeIn.addTransition(new Fade(Fade.OUT)).addTransition(new ChangeBounds()).addTransition(fadeIn);
- myTransition.addListener(new Transition.TransitionListenerAdapter() {
+ myTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionStart(Transition transition) {
System.out.println("---------ListView Tops: Before--------");