diff options
55 files changed, 1147 insertions, 757 deletions
diff --git a/api/current.txt b/api/current.txt index 850ffaaa364e..0ca9875ead82 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36128,7 +36128,7 @@ package android.service.notification { method public void deleteNotificationChannel(java.lang.String, java.lang.String); method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String); method public final void unsnoozeNotification(java.lang.String); method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); @@ -44081,8 +44081,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + 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 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); @@ -49026,8 +49026,6 @@ package android.widget { method public void addHeaderView(android.view.View); method public boolean areFooterDividersEnabled(); method public boolean areHeaderDividersEnabled(); - method protected android.view.View findViewTraversal(int); - method protected android.view.View findViewWithTagTraversal(java.lang.Object); method public android.widget.ListAdapter getAdapter(); method public deprecated long[] getCheckItemIds(); method public android.graphics.drawable.Drawable getDivider(); diff --git a/api/system-current.txt b/api/system-current.txt index 9050beac0ea5..594fa2ecac71 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -39157,7 +39157,7 @@ package android.service.notification { method public void deleteNotificationChannel(java.lang.String, java.lang.String); method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String); method public final void unsnoozeNotification(java.lang.String); method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); @@ -47485,8 +47485,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + 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 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); @@ -52794,8 +52794,6 @@ package android.widget { method public void addHeaderView(android.view.View); method public boolean areFooterDividersEnabled(); method public boolean areHeaderDividersEnabled(); - method protected android.view.View findViewTraversal(int); - method protected android.view.View findViewWithTagTraversal(java.lang.Object); method public android.widget.ListAdapter getAdapter(); method public deprecated long[] getCheckItemIds(); method public android.graphics.drawable.Drawable getDivider(); diff --git a/api/test-current.txt b/api/test-current.txt index 03942fdbc4b1..02616298a241 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -36263,7 +36263,7 @@ package android.service.notification { method public void deleteNotificationChannel(java.lang.String, java.lang.String); method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification); method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String); method public final void unsnoozeNotification(java.lang.String); method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel); @@ -44387,8 +44387,8 @@ package android.view { method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); - method public final android.view.View findViewById(int); - method public final android.view.View findViewWithTag(java.lang.Object); + 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 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); @@ -49343,8 +49343,6 @@ package android.widget { method public void addHeaderView(android.view.View); method public boolean areFooterDividersEnabled(); method public boolean areHeaderDividersEnabled(); - method protected android.view.View findViewTraversal(int); - method protected android.view.View findViewWithTagTraversal(java.lang.Object); method public android.widget.ListAdapter getAdapter(); method public deprecated long[] getCheckItemIds(); method public android.graphics.drawable.Drawable getDivider(); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 377e29d99576..000420f29c3b 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -162,7 +162,9 @@ int main(int argc, char** argv) uint8_t displayOrientation = configs[activeConfig].orientation; uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation]; - status_t result = screenshot.update(display, Rect(), 0, 0, 0, -1U, + status_t result = screenshot.update(display, Rect(), + 0 /* reqWidth */, 0 /* reqHeight */, + INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation); if (result == NO_ERROR) { base = screenshot.getPixels(); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index a8822c5be2f0..ef5bc5cf9075 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -623,7 +623,8 @@ public class RecoverySystem { final ConditionVariable condition = new ConditionVariable(); Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM, android.Manifest.permission.MASTER_CLEAR, new BroadcastReceiver() { diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 01d3391d32af..b26e32835aa1 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -34,6 +34,6 @@ oneway interface INotificationListener void onInterruptionFilterChanged(int interruptionFilter); // rankers only - void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user); + void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder); void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index cecdbee75b00..de86b2d2b6ed 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -77,12 +77,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS * A notification was posted by an app. Called before alert. * * @param sbn the new notification - * @param importance the initial importance of the notification. - * @param user true if the initial importance reflects an explicit user preference. * @return an adjustment or null to take no action, within 100ms. */ - abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, - int importance, boolean user); + abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn); /** * Updates a notification. N.B. this won’t cause @@ -202,8 +199,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { @Override - public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, - int importance, boolean user) { + public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) { StatusBarNotification sbn; try { sbn = sbnHolder.get(); @@ -214,8 +210,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; - args.argi1 = importance; - args.argi2 = user ? 1 : 0; mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, args).sendToTarget(); } @@ -254,10 +248,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS case MSG_ON_NOTIFICATION_ENQUEUED: { SomeArgs args = (SomeArgs) msg.obj; StatusBarNotification sbn = (StatusBarNotification) args.arg1; - final int importance = args.argi1; - final boolean user = args.argi2 == 1; args.recycle(); - Adjustment adjustment = onNotificationEnqueued(sbn, importance, user); + Adjustment adjustment = onNotificationEnqueued(sbn); if (adjustment != null) { if (!isBound()) return; try { diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 22ad83acf684..e5abdac9e860 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1100,8 +1100,8 @@ public abstract class NotificationListenerService extends Service { } @Override - public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder, - int importance, boolean user) throws RemoteException { + public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder) + throws RemoteException { // no-op in the listener } diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 4b02df86c82e..60d8a0fc8894 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -53,12 +53,16 @@ import java.lang.reflect.Array; * @param end End index in the source object. */ private final void copySpans(Spanned src, int start, int end) { - Object[] spans = src.getSpans(start, end, Object.class); + final Object[] spans = src.getSpans(start, end, Object.class); for (int i = 0; i < spans.length; i++) { + if (spans[i] instanceof NoCopySpan) { + continue; + } + int st = src.getSpanStart(spans[i]); int en = src.getSpanEnd(spans[i]); - int fl = src.getSpanFlags(spans[i]); + final int fl = src.getSpanFlags(spans[i]); if (st < start) st = start; @@ -78,33 +82,42 @@ import java.lang.reflect.Array; * @param end End index in the source object. */ private final void copySpans(SpannableStringInternal src, int start, int end) { - if (start == 0 && end == src.length()) { + int count = 0; + boolean includesNoCopySpan = false; + final int[] srcData = src.mSpanData; + final Object[] srcSpans = src.mSpans; + final int limit = src.mSpanCount; + + for (int i = 0; i < limit; i++) { + int spanStart = srcData[i * COLUMNS + START]; + int spanEnd = srcData[i * COLUMNS + END]; + if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; + if (srcSpans[i] instanceof NoCopySpan) { + includesNoCopySpan = true; + continue; + } + count++; + } + + if (count == 0) return; + + if (!includesNoCopySpan && start == 0 && end == src.length()) { mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length); mSpanData = new int[src.mSpanData.length]; mSpanCount = src.mSpanCount; System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length); System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length); } else { - int count = 0; - int[] srcData = src.mSpanData; - int limit = src.mSpanCount; - for (int i = 0; i < limit; i++) { - int spanStart = srcData[i * COLUMNS + START]; - int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; - count++; - } - - if (count == 0) return; - - Object[] srcSpans = src.mSpans; mSpanCount = count; mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount); mSpanData = new int[mSpans.length * COLUMNS]; for (int i = 0, j = 0; i < limit; i++) { int spanStart = srcData[i * COLUMNS + START]; int spanEnd = srcData[i * COLUMNS + END]; - if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue; + if (isOutOfCopyRange(start, end, spanStart, spanEnd) + || srcSpans[i] instanceof NoCopySpan) { + continue; + } if (spanStart < start) spanStart = start; if (spanEnd > end) spanEnd = end; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 5bb577f661a4..cf8da179bab5 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -34,7 +34,7 @@ public class SurfaceControl { private static final String TAG = "SurfaceControl"; private static native long nativeCreate(SurfaceSession session, String name, - int w, int h, int format, int flags, long parentObject) + int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid) throws OutOfResourcesException; private static native void nativeRelease(long nativeObject); private static native void nativeDestroy(long nativeObject); @@ -281,17 +281,25 @@ public class SurfaceControl { * @param h The surface initial height. * @param flags The surface creation flags. Should always include {@link #HIDDEN} * in the creation flags. + * @param windowType The type of the window as specified in WindowManager.java. + * @param ownerUid A unique per-app ID. * * @throws throws OutOfResourcesException If the SurfaceControl cannot be created. */ public SurfaceControl(SurfaceSession session, - String name, int w, int h, int format, int flags) + String name, int w, int h, int format, int flags, int windowType, int ownerUid) throws OutOfResourcesException { - this(session, name, w, h, format, flags, null); + this(session, name, w, h, format, flags, null, windowType, ownerUid); } public SurfaceControl(SurfaceSession session, - String name, int w, int h, int format, int flags, SurfaceControl parent) + String name, int w, int h, int format, int flags) + throws OutOfResourcesException { + this(session, name, w, h, format, flags, null, -1, -1); + } + + public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags, + SurfaceControl parent, int windowType, int ownerUid) throws OutOfResourcesException { if (session == null) { throw new IllegalArgumentException("session must not be null"); @@ -310,7 +318,8 @@ public class SurfaceControl { } mName = name; - mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0); + mNativeObject = nativeCreate(session, name, w, h, format, flags, + parent != null ? parent.mNativeObject : 0, windowType, ownerUid); if (mNativeObject == 0) { throw new OutOfResourcesException( "Couldn't allocate SurfaceControl native object"); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cbe43f7ba0a2..1292243d1b05 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -19987,38 +19987,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * {@hide} * @param id the id of the view to be found * @return the view of the specified id, null if cannot be found + * @hide */ - protected View findViewTraversal(@IdRes int id) { + protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { - return this; + return (T) this; } return null; } /** - * {@hide} * @param tag the tag of the view to be found * @return the view of specified tag, null if cannot be found + * @hide */ - protected View findViewWithTagTraversal(Object tag) { + protected <T extends View> T findViewWithTagTraversal(Object tag) { if (tag != null && tag.equals(mTag)) { - return this; + return (T) this; } return null; } /** - * {@hide} * @param predicate The predicate to evaluate. * @param childToSkip If not null, ignores this child during the recursive traversal. * @return The first view that matches the predicate or null. + * @hide */ - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.apply(this)) { - return this; + return (T) this; } return null; } @@ -20031,7 +20032,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The view that has the given id in the hierarchy or null */ @Nullable - public final View findViewById(@IdRes int id) { + public final <T extends View> T findViewById(@IdRes int id) { if (id < 0) { return null; } @@ -20044,11 +20045,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param accessibilityId The searched accessibility id. * @return The found view. */ - final View findViewByAccessibilityId(int accessibilityId) { + final <T extends View> T findViewByAccessibilityId(int accessibilityId) { if (accessibilityId < 0) { return null; } - View view = findViewByAccessibilityIdTraversal(accessibilityId); + T view = findViewByAccessibilityIdTraversal(accessibilityId); if (view != null) { return view.includeForAccessibility() ? view : null; } @@ -20067,12 +20068,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param accessibilityId The accessibility id. * @return The found view. - * * @hide */ - public View findViewByAccessibilityIdTraversal(int accessibilityId) { + public <T extends View> T findViewByAccessibilityIdTraversal(int accessibilityId) { if (getAccessibilityViewId() == accessibilityId) { - return this; + return (T) this; } return null; } @@ -20084,7 +20084,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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 View findViewWithTag(Object tag) { + public final <T extends View> T findViewWithTag(Object tag) { if (tag == null) { return null; } @@ -20092,19 +20092,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * {@hide} * Look for a child view that matches the specified predicate. * If this view matches the predicate, return this view. * * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. + * @hide */ - public final View findViewByPredicate(Predicate<View> predicate) { + public final <T extends View> T findViewByPredicate(Predicate<View> predicate) { return findViewByPredicateTraversal(predicate, null); } /** - * {@hide} * Look for a child view that matches the specified predicate, * starting with the specified view and its descendents and then * recusively searching the ancestors and siblings of that view @@ -20118,11 +20117,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param start The view to start from. * @param predicate The predicate to evaluate. * @return The first view that matches the predicate or null. + * @hide */ - public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) { + public final <T extends View> T findViewByPredicateInsideOut( + View start, Predicate<View> predicate) { View childToSkip = null; for (;;) { - View view = start.findViewByPredicateTraversal(predicate, childToSkip); + T view = start.findViewByPredicateTraversal(predicate, childToSkip); if (view != null || start == this) { return view; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 28b9dcc308b5..ab10ac17d01b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4211,9 +4211,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewTraversal(@IdRes int id) { + protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4226,7 +4226,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewById(id); if (v != null) { - return v; + return (T) v; } } } @@ -4238,9 +4238,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewWithTagTraversal(Object tag) { + protected <T extends View> T findViewWithTagTraversal(Object tag) { if (tag != null && tag.equals(mTag)) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4253,7 +4253,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewWithTag(tag); if (v != null) { - return v; + return (T) v; } } } @@ -4265,9 +4265,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@hide} */ @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.apply(this)) { - return this; + return (T) this; } final View[] where = mChildren; @@ -4280,7 +4281,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager v = v.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 8703468a74a7..0a73e17dd6e4 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -488,7 +488,8 @@ public class WebViewClient { * @param view The WebView which needs to be cleaned up. * @param detail the reason why it exited. * @return true if the host application handled the situation that process has - * exited, otherwise, application will crash. + * exited, otherwise, application will crash if render process crashed, + * or be killed if render process was killed by the system. */ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { return false; diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index 51587a74987d..9a39a17ce223 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -250,7 +250,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button); mDefaultActivityButton.setOnClickListener(mCallbacks); mDefaultActivityButton.setOnLongClickListener(mCallbacks); - mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image); + mDefaultActivityButtonImage = mDefaultActivityButton.findViewById(R.id.image); final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button); expandButton.setOnClickListener(mCallbacks); @@ -282,7 +282,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod mExpandActivityOverflowButton = expandButton; mExpandActivityOverflowButtonImage = - (ImageView) expandButton.findViewById(R.id.image); + expandButton.findViewById(R.id.image); mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable); mAdapter = new ActivityChooserViewAdapter(); @@ -760,7 +760,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod convertView = LayoutInflater.from(getContext()).inflate( R.layout.activity_chooser_view_list_item, parent, false); convertView.setId(ITEM_VIEW_TYPE_FOOTER); - TextView titleView = (TextView) convertView.findViewById(R.id.title); + TextView titleView = convertView.findViewById(R.id.title); titleView.setText(mContext.getString( R.string.activity_chooser_view_see_all)); } @@ -772,11 +772,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } PackageManager packageManager = mContext.getPackageManager(); // Set the icon - ImageView iconView = (ImageView) convertView.findViewById(R.id.icon); + ImageView iconView = convertView.findViewById(R.id.icon); ResolveInfo activity = (ResolveInfo) getItem(position); iconView.setImageDrawable(activity.loadIcon(packageManager)); // Set the title. - TextView titleView = (TextView) convertView.findViewById(R.id.title); + TextView titleView = 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 68e6809a3f68..06d486813b1b 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -451,7 +451,7 @@ public class AppSecurityPermissions { private View getPermissionsView(int which, boolean showRevokeUI) { LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); - LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list); + LinearLayout displayList = permsView.findViewById(R.id.perms_list); View noPermsView = permsView.findViewById(R.id.no_permissions); displayPermissions(mPermGroupsList, displayList, which, showRevokeUI); @@ -517,8 +517,8 @@ public class AppSecurityPermissions { CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) { View permView = inflater.inflate(R.layout.app_permission_item_old, null); - TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); - TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); + TextView permGrpView = permView.findViewById(R.id.permission_group); + TextView permDescView = 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 bbc50dafa576..81f0d3d0a352 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -388,7 +388,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp text = (TextView) view; } else { // Otherwise, find the TextView field within the layout - text = (TextView) view.findViewById(mFieldId); + text = 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 557d41179921..1b899dbf6d03 100644 --- a/core/java/android/widget/CalendarViewLegacyDelegate.java +++ b/core/java/android/widget/CalendarViewLegacyDelegate.java @@ -316,9 +316,9 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega View content = layoutInflater.inflate(R.layout.calendar_view, null, false); mDelegator.addView(content); - mListView = (ListView) mDelegator.findViewById(R.id.list); - mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names); - mMonthName = (TextView) content.findViewById(R.id.month_name); + mListView = mDelegator.findViewById(R.id.list); + mDayNamesHeader = content.findViewById(R.id.day_names); + mMonthName = 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 f712685ec3e0..907250aa5598 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -115,10 +115,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mDelegator.addView(mContainer); // Set up header views. - final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header); - mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year); + final ViewGroup header = mContainer.findViewById(R.id.date_picker_header); + mHeaderYear = header.findViewById(R.id.date_picker_header_year); mHeaderYear.setOnClickListener(mOnHeaderClickListener); - mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date); + mHeaderMonthDay = 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 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { a.recycle(); // Set up picker container. - mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator); + mAnimator = mContainer.findViewById(R.id.animator); // Set up day picker view. - mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker); + mDayPickerView = mAnimator.findViewById(R.id.date_picker_day_picker); mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek); mDayPickerView.setMinDate(mMinDate.getTimeInMillis()); mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis()); @@ -165,7 +165,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener); // Set up year picker view. - mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker); + mYearPickerView = 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 8d5bf8f7bce4..63621e123320 100644 --- a/core/java/android/widget/DayPickerPagerAdapter.java +++ b/core/java/android/widget/DayPickerPagerAdapter.java @@ -225,7 +225,7 @@ class DayPickerPagerAdapter extends PagerAdapter { public Object instantiateItem(ViewGroup container, int position) { final View itemView = mInflater.inflate(mLayoutResId, container, false); - final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId); + final SimpleMonthView v = 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 94022ae11b6d..058baa628d46 100644 --- a/core/java/android/widget/DayPickerViewPager.java +++ b/core/java/android/widget/DayPickerViewPager.java @@ -137,9 +137,10 @@ class DayPickerViewPager extends ViewPager { } @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate, + View childToSkip) { if (predicate.apply(this)) { - return this; + return (T) this; } // Always try the selected view first. @@ -148,7 +149,7 @@ class DayPickerViewPager extends ViewPager { if (current != childToSkip && current != null) { final View v = current.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } @@ -160,7 +161,7 @@ class DayPickerViewPager extends ViewPager { final View v = child.findViewByPredicate(predicate); if (v != null) { - return v; + return (T) v; } } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index e52c13b19534..b7da04e9786a 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3735,32 +3735,30 @@ public class ListView extends AbsListView { } } - /* (non-Javadoc) + /** * @see android.view.View#findViewById(int) - * First look in our children, then in any header and footer views that may be scrolled off. + * @removed For internal use only. This should have been hidden. */ @Override - protected View findViewTraversal(@IdRes int id) { - View v; - v = super.findViewTraversal(id); + 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); if (v == null) { v = findViewInHeadersOrFooters(mHeaderViewInfos, id); if (v != null) { - return v; + return (T) v; } v = findViewInHeadersOrFooters(mFooterViewInfos, id); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } - /* (non-Javadoc) - * - * Look in the passed in list of headers or footers for the view. - */ View findViewInHeadersOrFooters(ArrayList<FixedViewInfo> where, int id) { + // Look in the passed in list of headers or footers for the view. if (where != null) { int len = where.size(); View v; @@ -3780,33 +3778,32 @@ public class ListView extends AbsListView { return null; } - /* (non-Javadoc) + /** * @see android.view.View#findViewWithTag(Object) - * First look in our children, then in any header and footer views that may be scrolled off. + * @removed For internal use only. This should have been hidden. */ @Override - protected View findViewWithTagTraversal(Object tag) { - View v; - v = super.findViewWithTagTraversal(tag); + 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); if (v == null) { v = findViewWithTagInHeadersOrFooters(mHeaderViewInfos, tag); if (v != null) { - return v; + return (T) v; } v = findViewWithTagInHeadersOrFooters(mFooterViewInfos, tag); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } - /* (non-Javadoc) - * - * Look in the passed in list of headers or footers for the view with the tag. - */ View findViewWithTagInHeadersOrFooters(ArrayList<FixedViewInfo> where, Object tag) { + // Look in the passed in list of headers or footers for the view with + // the tag. if (where != null) { int len = where.size(); View v; @@ -3827,32 +3824,33 @@ public class ListView extends AbsListView { } /** - * @hide + * First look in our children, then in any header and footer views that may + * be scrolled off. + * * @see android.view.View#findViewByPredicate(Predicate) - * First look in our children, then in any header and footer views that may be scrolled off. + * @hide */ @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { - View v; - v = super.findViewByPredicateTraversal(predicate, childToSkip); + protected <T extends View> T findViewByPredicateTraversal( + Predicate<View> predicate, View childToSkip) { + View v = super.findViewByPredicateTraversal(predicate, childToSkip); if (v == null) { v = findViewByPredicateInHeadersOrFooters(mHeaderViewInfos, predicate, childToSkip); if (v != null) { - return v; + return (T) v; } v = findViewByPredicateInHeadersOrFooters(mFooterViewInfos, predicate, childToSkip); if (v != null) { - return v; + return (T) v; } } - return v; + return (T) v; } - /* (non-Javadoc) - * - * Look in the passed in list of headers or footers for the first view that matches - * the predicate. + /** + * Look in the passed in list of headers or footers for the first view that + * matches the predicate. */ View findViewByPredicateInHeadersOrFooters(ArrayList<FixedViewInfo> where, Predicate<View> predicate, View childToSkip) { diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 80086374ebd3..8e04f1cd9f5b 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -257,13 +257,13 @@ public class MediaController extends FrameLayout { .getText(com.android.internal.R.string.lockscreen_transport_play_description); mPauseDescription = res .getText(com.android.internal.R.string.lockscreen_transport_pause_description); - mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause); + mPauseButton = v.findViewById(com.android.internal.R.id.pause); if (mPauseButton != null) { mPauseButton.requestFocus(); mPauseButton.setOnClickListener(mPauseListener); } - mFfwdButton = (ImageButton) v.findViewById(com.android.internal.R.id.ffwd); + mFfwdButton = v.findViewById(com.android.internal.R.id.ffwd); if (mFfwdButton != null) { mFfwdButton.setOnClickListener(mFfwdListener); if (!mFromXml) { @@ -271,7 +271,7 @@ public class MediaController extends FrameLayout { } } - mRewButton = (ImageButton) v.findViewById(com.android.internal.R.id.rew); + mRewButton = v.findViewById(com.android.internal.R.id.rew); if (mRewButton != null) { mRewButton.setOnClickListener(mRewListener); if (!mFromXml) { @@ -280,16 +280,16 @@ public class MediaController extends FrameLayout { } // By default these are hidden. They will be enabled when setPrevNextListeners() is called - mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next); + mNextButton = v.findViewById(com.android.internal.R.id.next); if (mNextButton != null && !mFromXml && !mListenersSet) { mNextButton.setVisibility(View.GONE); } - mPrevButton = (ImageButton) v.findViewById(com.android.internal.R.id.prev); + mPrevButton = v.findViewById(com.android.internal.R.id.prev); if (mPrevButton != null && !mFromXml && !mListenersSet) { mPrevButton.setVisibility(View.GONE); } - mProgress = (ProgressBar) v.findViewById(com.android.internal.R.id.mediacontroller_progress); + mProgress = v.findViewById(com.android.internal.R.id.mediacontroller_progress); if (mProgress != null) { if (mProgress instanceof SeekBar) { SeekBar seeker = (SeekBar) mProgress; @@ -298,8 +298,8 @@ public class MediaController extends FrameLayout { mProgress.setMax(1000); } - mEndTime = (TextView) v.findViewById(com.android.internal.R.id.time); - mCurrentTime = (TextView) v.findViewById(com.android.internal.R.id.time_current); + mEndTime = v.findViewById(com.android.internal.R.id.time); + mCurrentTime = 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/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 359d04e71969..5505f2fef919 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1578,7 +1578,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { final Context context = root.getContext(); - final ViewGroup target = (ViewGroup) root.findViewById(viewId); + final ViewGroup target = root.findViewById(viewId); if (target == null) return; if (nestedViews != null) { // Inflate nested views and add as children @@ -1757,7 +1757,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { if (isRelative) { @@ -1857,7 +1857,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); } @@ -2045,7 +2045,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { - final TextView target = (TextView) root.findViewById(viewId); + final TextView target = 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 f833d1b4a9d6..fbb89930a035 100644 --- a/core/java/android/widget/SuggestionsAdapter.java +++ b/core/java/android/widget/SuggestionsAdapter.java @@ -286,7 +286,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene v.setTag(new ChildViewCache(v)); // Set up icon. - final ImageView iconRefine = (ImageView) v.findViewById(R.id.edit_query); + final ImageView iconRefine = v.findViewById(R.id.edit_query); iconRefine.setImageResource(mCommitIconResId); return v; @@ -304,11 +304,11 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene public final ImageView mIconRefine; public ChildViewCache(View v) { - 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); + 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); } } diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index 32418cdb8240..7e2cadfc8c45 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -619,7 +619,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); mTabWidget, // tab widget is the parent false); // no inflate params - final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); + final TextView tv = tabIndicator.findViewById(R.id.title); tv.setText(mLabel); if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) { @@ -653,8 +653,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); mTabWidget, // tab widget is the parent false); // no inflate params - final TextView tv = (TextView) tabIndicator.findViewById(R.id.title); - final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon); + final TextView tv = tabIndicator.findViewById(R.id.title); + final ImageView iconView = 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/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 6a68f60b2c7a..4634631285e9 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -86,7 +86,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { inflater.inflate(layoutResourceId, mDelegator, true); // hour - mHourSpinner = (NumberPicker) delegator.findViewById(R.id.hour); + mHourSpinner = delegator.findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { updateInputState(); @@ -100,17 +100,17 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input); + mHourSpinnerInput = mHourSpinner.findViewById(R.id.numberpicker_input); mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); // divider (only for the new widget style) - mDivider = (TextView) mDelegator.findViewById(R.id.divider); + mDivider = mDelegator.findViewById(R.id.divider); if (mDivider != null) { setDividerText(); } // minute - mMinuteSpinner = (NumberPicker) mDelegator.findViewById(R.id.minute); + mMinuteSpinner = mDelegator.findViewById(R.id.minute); mMinuteSpinner.setMinValue(0); mMinuteSpinner.setMaxValue(59); mMinuteSpinner.setOnLongPressUpdateInterval(100); @@ -138,7 +138,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input); + mMinuteSpinnerInput = 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 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { onTimeChanged(); } }); - mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); + mAmPmSpinnerInput = mAmPmSpinner.findViewById(R.id.numberpicker_input); mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); } if (isAmPmAtStart()) { // Move the am/pm view to the beginning - ViewGroup amPmParent = (ViewGroup) delegator.findViewById(R.id.timePickerLayout); + ViewGroup amPmParent = 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 diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 789e60b62799..bf0601d2851d 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -299,7 +299,7 @@ public class Toast { if (mNextView == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } - TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message); + TextView tv = 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 69b799716f02..1a3ca8667e39 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -264,7 +264,7 @@ public class ZoomButtonsController implements View.OnTouchListener { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(com.android.internal.R.layout.zoom_container, container); - mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls); + mControls = 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/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java index 4fd19c37bf47..53fa7ab93133 100644 --- a/core/java/com/android/internal/widget/WatchHeaderListView.java +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -92,13 +92,14 @@ public class WatchHeaderListView extends ListView { } @Override - protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { + protected <T extends View> T findViewByPredicateTraversal( + Predicate<View> predicate, View childToSkip) { View v = super.findViewByPredicateTraversal(predicate, childToSkip); if (v == null && mTopPanel != null && mTopPanel != childToSkip && !mTopPanel.isRootNamespace()) { - return mTopPanel.findViewByPredicate(predicate); + return (T) mTopPanel.findViewByPredicate(predicate); } - return v; + return (T) v; } @Override diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ab3e3113e1a2..0171562184dc 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -98,16 +98,18 @@ static struct { // ---------------------------------------------------------------------------- static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, - jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject) { + jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject, + jint windowType, jint ownerUid) { ScopedUtfChars name(env, nameStr); sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject); sp<SurfaceControl> surface = client->createSurface( - String8(name.c_str()), w, h, format, flags, parent); + String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid); if (surface == NULL) { jniThrowException(env, OutOfResourcesException, NULL); return 0; } + surface->incStrong((void *)nativeCreate); return reinterpret_cast<jlong>(surface.get()); } @@ -260,7 +262,7 @@ static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, } ScreenshotClient::capture(displayToken, consumer->getIGraphicBufferProducer(), sourceCrop, - width, height, uint32_t(minLayer), uint32_t(maxLayer), + width, height, minLayer, maxLayer, useIdentityTransform); } } @@ -742,7 +744,7 @@ static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject token // ---------------------------------------------------------------------------- static const JNINativeMethod sSurfaceControlMethods[] = { - {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJ)J", + {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJII)J", (void*)nativeCreate }, {"nativeRelease", "(J)V", (void*)nativeRelease }, diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml new file mode 100644 index 000000000000..10b647044c73 --- /dev/null +++ b/core/res/res/values-mcc704-mnc01/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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 my 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> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + <string-array name="config_twoDigitNumberPattern"> + <item>"*1"</item> + <item>"*5"</item> + <item>"*9"</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml new file mode 100755 index 000000000000..7b7c48d46b14 --- /dev/null +++ b/core/res/res/values-mcc708-mnc001/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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 my 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <string-array translatable="false" name="config_twoDigitNumberPattern"> + <item>"*1"</item> + <item>"*5"</item> + </string-array> +</resources> diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index e17a3a6ff0a1..c7d0fa5b0cf3 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -116,7 +116,10 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab LoadedArscEntry* out_entry, ResTable_config* out_selected_config, uint32_t* out_flags) const { ATRACE_CALL(); - const TypeSpecPtr& ptr = type_specs_[type_idx]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; if (ptr == nullptr) { return false; } @@ -334,6 +337,15 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { loaded_package->dynamic_ = true; } + if (header->header.headerSize >= sizeof(ResTable_package)) { + uint32_t type_id_offset = dtohl(header->typeIdOffset); + if (type_id_offset > std::numeric_limits<uint8_t>::max()) { + LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large."; + return {}; + } + loaded_package->type_id_offset_ = static_cast<int>(type_id_offset); + } + util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); @@ -385,7 +397,6 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); types_builder = {}; @@ -403,6 +414,12 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) { return {}; } + if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) > + std::numeric_limits<uint8_t>::max()) { + LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID."; + return {}; + } + // The data portion of this chunk contains entry_count 32bit entries, // each one representing a set of flags. // Here we only validate that the chunk is well formed. diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 8362008eccc7..e8cb164ce201 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -94,6 +94,7 @@ class LoadedPackage { std::string package_name_; int package_id_ = -1; bool dynamic_ = false; + int type_id_offset_ = 0; ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 045507e2e277..f8aa61aa588a 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -139,6 +139,36 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { EXPECT_EQ(0x7f, packages[0]->GetPackageId()); } +TEST(LoadedArscTest, LoadFeatureSplit) { + std::string contents; + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", + &contents)); + std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size()); + ASSERT_NE(nullptr, loaded_arsc); + + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + LoadedArscEntry entry; + ResTable_config selected_config; + uint32_t flags; + + ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry, + &selected_config, &flags)); + + size_t len; + const char16_t* type_name16 = entry.type_string_ref.string16(&len); + ASSERT_NE(nullptr, type_name16); + ASSERT_NE(0u, len); + + size_t utf8_len = utf16_to_utf8_length(type_name16, len); + std::string type_name; + type_name.resize(utf8_len); + utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1); + + EXPECT_EQ(std::string("string"), type_name); +} + // structs with size fields (like Res_value, ResTable_entry) should be // backwards and forwards compatible (aka checking the size field against // sizeof(Res_value) might not be backwards compatible. diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk Binary files differindex 04940fb9bce2..767fed697034 100644 --- a/libs/androidfw/tests/data/feature/feature.apk +++ b/libs/androidfw/tests/data/feature/feature.apk diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp new file mode 100644 index 000000000000..ad0b1f15abff --- /dev/null +++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#include "TestSceneBase.h" +#include <string> +#include <hwui/Paint.h> +#include <minikin/Layout.h> + +class SaveLayer2Animation; + +static TestScene::Registrar _SaveLayer(TestScene::Info{ + "savelayer2", + "Interleaving 20 drawText/drawRect ops with saveLayer" + "Tests the clipped saveLayer performance and FBO switching overhead.", + TestScene::simpleCreateScene<SaveLayer2Animation> +}); + +class SaveLayer2Animation : public TestScene { +public: + Paint mBluePaint; + Paint mGreenPaint; + + void createContent(int width, int height, Canvas& canvas) override { + canvas.drawColor(SkColorSetARGB(255, 255, 0, 0), SkBlendMode::kSrcOver); + SkIRect bounds = SkIRect::MakeWH(width, height); + int regions = 20; + int smallRectHeight = (bounds.height()/regions); + int padding = smallRectHeight / 4; + int top = bounds.fTop; + + mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255)); + mBluePaint.setTextSize(padding); + mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0)); + mGreenPaint.setTextSize(padding); + + //interleave drawText and drawRect with saveLayer ops + for (int i = 0; i < regions; i++, top += smallRectHeight) { + canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding, + &mBluePaint, SaveFlags::ClipToLayer | SaveFlags::MatrixClip); + canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver); + std::string stri = std::to_string(i); + std::string offscreen = "offscreen line " + stri; + std::unique_ptr<uint16_t[]> offtext = TestUtils::asciiToUtf16(offscreen.c_str()); + canvas.drawText(offtext.get(), 0, offscreen.length(), offscreen.length(), + bounds.fLeft, top + padding, minikin::kBidi_Force_LTR, mBluePaint, nullptr); + canvas.restore(); + + canvas.drawRect(bounds.fLeft, top + padding, bounds.fRight, + top + smallRectHeight - padding, mBluePaint); + std::string onscreen = "onscreen line " + stri; + std::unique_ptr<uint16_t[]> ontext = TestUtils::asciiToUtf16(onscreen.c_str()); + canvas.drawText(ontext.get(), 0, onscreen.length(), onscreen.length(), bounds.fLeft, + top + smallRectHeight - padding, minikin::kBidi_Force_LTR, mGreenPaint, + nullptr); + } + } + void doFrame(int frameNr) override { + } +}; diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java index 21786c917763..13bbc3382ebf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java +++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java @@ -15,21 +15,28 @@ */ package com.android.settingslib; +import android.Manifest; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.Xml; -import android.provider.Settings; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.view.InflateException; import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; @@ -53,6 +60,20 @@ public class SuggestionParser { // If defined and not true, do not should optional step. private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported"; + // If defined, only display this optional step if the current user is of that type. + private static final String META_DATA_REQUIRE_USER_TYPE = + "com.android.settings.require_user_type"; + + // If defined, only display this optional step if a connection is available. + private static final String META_DATA_IS_CONNECTION_REQUIRED = + "com.android.settings.require_connection"; + + // The valid values that setup wizard recognizes for differentiating user types. + private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary"; + private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin"; + private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest"; + private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted"; + /** * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed. * For instance: @@ -75,7 +96,7 @@ public class SuggestionParser { private final Context mContext; private final List<SuggestionCategory> mSuggestionList; - private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>(); + private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>(); private final SharedPreferences mSharedPrefs; public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) { @@ -85,6 +106,14 @@ public class SuggestionParser { mSharedPrefs = sharedPrefs; } + @VisibleForTesting + public SuggestionParser(Context context, SharedPreferences sharedPrefs) { + mContext = context; + mSuggestionList = new ArrayList<SuggestionCategory>(); + mSharedPrefs = sharedPrefs; + Log.wtf(TAG, "Only use this constructor for testing"); + } + public List<Tile> getSuggestions() { List<Tile> suggestions = new ArrayList<>(); final int N = mSuggestionList.size(); @@ -111,23 +140,30 @@ public class SuggestionParser { return false; } - private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) { - int countBefore = suggestions.size(); - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(category.category); - if (category.pkg != null) { - intent.setPackage(category.pkg); - } - TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, - addCache, null, suggestions, true, false); + @VisibleForTesting + public void filterSuggestions(List<Tile> suggestions, int countBefore) { for (int i = countBefore; i < suggestions.size(); i++) { if (!isAvailable(suggestions.get(i)) || !isSupported(suggestions.get(i)) || + !satisifesRequiredUserType(suggestions.get(i)) || !satisfiesRequiredAccount(suggestions.get(i)) || + !satisfiesConnectivity(suggestions.get(i)) || isDismissed(suggestions.get(i))) { suggestions.remove(i--); } } + } + + private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) { + int countBefore = suggestions.size(); + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category.category); + if (category.pkg != null) { + intent.setPackage(category.pkg); + } + TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, + mAddCache, null, suggestions, true, false); + filterSuggestions(suggestions, countBefore); if (!category.multiple && suggestions.size() > (countBefore + 1)) { // If there are too many, remove them all and only re-add the one with the highest // priority. @@ -146,32 +182,77 @@ public class SuggestionParser { } private boolean isAvailable(Tile suggestion) { - String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE); - if (featureRequired != null) { - return mContext.getPackageManager().hasSystemFeature(featureRequired); + final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE); + if (featuresRequired != null) { + for (String feature : featuresRequired.split(",")) { + if (TextUtils.isEmpty(feature)) { + Log.w(TAG, "Found empty substring when parsing required features: " + + featuresRequired); + } else if (!mContext.getPackageManager().hasSystemFeature(feature)) { + Log.i(TAG, suggestion.title + " requires unavailable feature " + feature); + return false; + } + } + } + return true; + } + + @RequiresPermission(Manifest.permission.MANAGE_USERS) + private boolean satisifesRequiredUserType(Tile suggestion) { + final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE); + if (requiredUser != null) { + final UserManager userManager = mContext.getSystemService(UserManager.class); + UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId()); + for (String userType : requiredUser.split("\\|")) { + final boolean primaryUserCondtionMet = userInfo.isPrimary() + && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType); + final boolean adminUserConditionMet = userInfo.isAdmin() + && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType); + final boolean guestUserCondtionMet = userInfo.isGuest() + && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType); + final boolean restrictedUserCondtionMet = userInfo.isRestricted() + && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType); + if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet + || restrictedUserCondtionMet) { + return true; + } + } + Log.i(TAG, suggestion.title + " requires user type " + requiredUser); + return false; } return true; } public boolean satisfiesRequiredAccount(Tile suggestion) { - String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT); + final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT); if (requiredAccountType == null) { return true; } AccountManager accountManager = AccountManager.get(mContext); Account[] accounts = accountManager.getAccountsByType(requiredAccountType); - return accounts.length > 0; + boolean satisfiesRequiredAccount = accounts.length > 0; + if (!satisfiesRequiredAccount) { + Log.i(TAG, suggestion.title + " requires unavailable account type " + + requiredAccountType); + } + return satisfiesRequiredAccount; } public boolean isSupported(Tile suggestion) { - int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED); + final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED); try { if (suggestion.intent == null) { return false; } final Resources res = mContext.getPackageManager().getResourcesForActivity( suggestion.intent.getComponent()); - return isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true; + boolean isSupported = + isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true; + if (!isSupported) { + Log.i(TAG, suggestion.title + " requires unsupported resource " + + isSupportedResource); + } + return isSupported; } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent()); return false; @@ -181,6 +262,22 @@ public class SuggestionParser { } } + private boolean satisfiesConnectivity(Tile suggestion) { + final boolean isConnectionRequired = + suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED); + if (!isConnectionRequired) { + return true; + } + ConnectivityManager cm = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getActiveNetworkInfo(); + boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting(); + if (!satisfiesConnectivity) { + Log.i(TAG, suggestion.title + " is missing required connection."); + } + return satisfiesConnectivity; + } + public boolean isCategoryDone(String category) { String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category; return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index 703bc17c15c4..a77c31045662 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License */ - package com.android.settingslib.wifi; import android.content.Context; @@ -21,8 +20,8 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.StateListDrawable; +import android.net.NetworkBadging; import android.net.ScoredNetwork; import android.net.wifi.WifiConfiguration; import android.os.Looper; @@ -48,12 +47,9 @@ public class AccessPointPreference extends Preference { R.attr.state_encrypted, R.attr.state_saved }; - private static final int[] STATE_NONE = {}; - private static final int[] wifi_signal_attributes = { R.attr.wifi_signal }; private static final int[] wifi_friction_attributes = { R.attr.wifi_friction }; - private final StateListDrawable mWifiSld; private final StateListDrawable mFrictionSld; private final int mBadgePadding; private final UserBadgeCache mBadgeCache; @@ -65,8 +61,6 @@ public class AccessPointPreference extends Preference { private int mLevel; private CharSequence mContentDescription; private int mDefaultIconResId; - private int mIconWidth; - private int mIconHeight; private int mWifiBadge = ScoredNetwork.BADGING_NONE; static final int[] WIFI_CONNECTION_STRENGTH = { @@ -79,7 +73,6 @@ public class AccessPointPreference extends Preference { // Used for dummy pref. public AccessPointPreference(Context context, AttributeSet attrs) { super(context, attrs); - mWifiSld = null; mFrictionSld = null; mBadgePadding = 0; mBadgeCache = null; @@ -95,11 +88,6 @@ public class AccessPointPreference extends Preference { mAccessPoint.setTag(this); mLevel = -1; - mWifiSld = (StateListDrawable) context.getTheme() - .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); - // Save icon width and height to use for creating a badged icon - setIconWidthAndHeight(); - TypedArray frictionSld; try { frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes); @@ -126,11 +114,6 @@ public class AccessPointPreference extends Preference { mLevel = -1; mDefaultIconResId = iconResId; - mWifiSld = (StateListDrawable) context.getTheme() - .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); - // Save icon width and height to use for creating a badged icon - setIconWidthAndHeight(); - TypedArray frictionSld; try { frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes); @@ -145,13 +128,6 @@ public class AccessPointPreference extends Preference { .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding); } - private void setIconWidthAndHeight() { - // TODO(sghuman): Refactor this defined widths and heights into a dimension resource and - // reference directly. - mIconWidth = mWifiSld.getIntrinsicWidth(); - mIconHeight = mWifiSld.getIntrinsicHeight(); - } - public AccessPoint getAccessPoint() { return mAccessPoint; } @@ -185,35 +161,15 @@ public class AccessPointPreference extends Preference { protected void updateIcon(int level, Context context) { if (level == -1) { safeSetDefaultIcon(); + return; + } + TronUtils.logWifiSettingsBadge(context, mWifiBadge); + Drawable drawable = NetworkBadging.getWifiIcon(level, mWifiBadge, getContext().getTheme()); + if (!mForSavedNetworks && drawable != null) { + drawable.setTint(Utils.getColorAccent(getContext())); + setIcon(drawable); } else { - TronUtils.logWifiSettingsBadge(context, mWifiBadge); - if (mWifiBadge != ScoredNetwork.BADGING_NONE) { - // TODO(sghuman): Refactor this to reuse drawable to save memory and add to a - // special subclass of AccessPointPreference - LayerDrawable drawable = Utils.getBadgedWifiIcon(context, level, mWifiBadge); - drawable.setLayerSize(0, mIconWidth, mIconHeight); - drawable.setLayerSize(1, mIconWidth, mIconHeight); - drawable.setTint(Utils.getColorAccent(getContext())); - setIcon(drawable); - return; - } - if (getIcon() == null) { - // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then - // set the icon (drawable) to that state's drawable. - // If sld is null then we are indexing and therefore do not have access to - // (nor need to display) the drawable. - if (mWifiSld != null) { - mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) - ? STATE_SECURED - : STATE_NONE); - Drawable drawable = mWifiSld.getCurrent(); - if (!mForSavedNetworks && drawable != null) { - setIcon(drawable); - return; - } - } - safeSetDefaultIcon(); - } + safeSetDefaultIcon(); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index c95cac5c8c67..021a96cedd6d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -34,9 +34,11 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings.Global; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Pair; +import com.android.settingslib.SuggestionParser; import com.android.settingslib.TestConfig; import static org.mockito.Mockito.atLeastOnce; @@ -156,6 +158,32 @@ public class TileUtilsTest { } @Test + public void getTilesForIntent_shouldSkipFilteredApps() { + final String testCategory = "category1"; + Intent intent = new Intent(); + Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); + List<Tile> outTiles = new ArrayList<>(); + List<ResolveInfo> info = new ArrayList<>(); + ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, + URI_GET_SUMMARY); + addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google"); + addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true"); + info.add(resolveInfo); + + when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) + .thenReturn(info); + + TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, + null /* defaultCategory */, outTiles, false /* usePriority */, + false /* checkCategory */); + + assertThat(outTiles.size()).isEqualTo(1); + SuggestionParser parser = new SuggestionParser(mContext, null); + parser.filterSuggestions(outTiles, 0); + assertThat(outTiles.size()).isEqualTo(0); + } + + @Test public void getCategories_shouldHandleExtraIntentAction() { final String testCategory = "category1"; final String testAction = "action1"; @@ -309,4 +337,16 @@ public class TileUtilsTest { } return info; } + + private void addMetadataToInfo(ResolveInfo info, String key, String value) { + if (!TextUtils.isEmpty(key)) { + if (info.activityInfo == null) { + info.activityInfo = new ActivityInfo(); + } + if (info.activityInfo.metaData == null) { + info.activityInfo.metaData = new Bundle(); + } + info.activityInfo.metaData.putString(key, value); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java index 02d1cc14fc41..4f175368aceb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java @@ -74,11 +74,8 @@ public class TaskGridLayoutAlgorithm { yOffsets = new int[taskCount]; int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount); - - tasksPerLine = layoutTaskCount < 2 ? 1 : ( - layoutTaskCount < 5 ? 2 : ( - layoutTaskCount < 7 ? 3 : 4)); - lines = layoutTaskCount < 3 ? 1 : 2; + tasksPerLine = getTasksPerLine(layoutTaskCount); + lines = layoutTaskCount < 4 ? 1 : 2; // A couple of special cases. boolean landscapeWindow = mWindowRect.width() > mWindowRect.height(); @@ -131,6 +128,27 @@ public class TaskGridLayoutAlgorithm { emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex; } } + + private int getTasksPerLine(int taskCount) { + switch(taskCount) { + case 0: + return 0; + case 1: + return 1; + case 2: + case 4: + return 2; + case 3: + case 5: + case 6: + return 3; + case 7: + case 8: + return 4; + default: + throw new IllegalArgumentException("Unsupported task count " + taskCount); + } + } } /** diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index 8ce52782d114..58edadcda257 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -36,6 +36,7 @@ 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; import android.os.ShellCallback; @@ -81,7 +82,6 @@ public final class AutoFillManagerService extends SystemService { protected static final int MSG_REQUEST_AUTO_FILL = 3; private final AutoFillManagerServiceStub mServiceStub; - private final AutoFillUI mUi; private final Context mContext; private final ContentResolver mResolver; @@ -146,7 +146,7 @@ public final class AutoFillManagerService extends SystemService { mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true); mContext = context; - mUi = new AutoFillUI(context, this, mLock); + mResolver = context.getContentResolver(); mServiceStub = new AutoFillManagerServiceStub(); } @@ -186,7 +186,7 @@ public final class AutoFillManagerService extends SystemService { if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent); return null; } - return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, mRequestsHistory, + return new AutoFillManagerServiceImpl(this, mContext, mLock, mRequestsHistory, FgThread.getHandler(), userId, serviceInfo.applicationInfo.uid, serviceComponent, SERVICE_BINDING_LIFETIME_MS); } @@ -288,6 +288,11 @@ public final class AutoFillManagerService extends SystemService { final IBinder activityToken = LocalServices.getService(ActivityManagerInternal.class) .getTopVisibleActivity(uid); if (activityToken == null) { + // TODO(b/33197203, b/34819567, b/34171325): figure out proper way to handle it + if (uid == Process.SYSTEM_UID) { + if (DEBUG) Log.w(TAG, "requestAutoFill(): ignoring call from system"); + return; + } throw new SecurityException("uid " + uid + " does not own the top activity"); } @@ -326,7 +331,6 @@ public final class AutoFillManagerService extends SystemService { } } } - mUi.dump(pw); pw.println("Requests history:"); mRequestsHistory.reverseDump(fd, pw, args); } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index 0dd891cd4235..2dcb31ccc229 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -19,7 +19,9 @@ 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.View.AUTO_FILL_FLAG_TYPE_FILL; import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; +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; @@ -56,6 +58,7 @@ import android.service.autofill.IAutoFillAppCallback; import android.service.autofill.IAutoFillServerCallback; import android.service.autofill.IAutoFillService; import android.service.voice.VoiceInteractionSession; +import android.util.ArrayMap; import android.util.LocalLog; import android.util.Log; import android.util.PrintWriterPrinter; @@ -63,6 +66,7 @@ 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; import android.view.autofill.FillResponse; @@ -74,6 +78,7 @@ import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Map; /** * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the @@ -96,12 +101,14 @@ final class AutoFillManagerServiceImpl { private final Object mLock; private final AutoFillServiceInfo mInfo; private final AutoFillManagerService mManagerService; - private final AutoFillUI mUi; // 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<>(); @@ -204,11 +211,10 @@ final class AutoFillManagerServiceImpl { // Estimated time when the service will be evicted from the cache. long mEstimateTimeOfDeath; - AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui, - Context context, Object lock, LocalLog requestsHistory, Handler handler, int userId, - int uid, ComponentName component, long ttl) { + AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock, + LocalLog requestsHistory, Handler handler, int userId, int uid, ComponentName component, + long ttl) { mManagerService = managerService; - mUi = ui; mContext = context; mLock = lock; mRequestsHistory = requestsHistory; @@ -277,8 +283,8 @@ final class AutoFillManagerServiceImpl { requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true); } - private void requestAutoFillLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds, - int flags, boolean queueIfNecessary) { + 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"); @@ -294,56 +300,35 @@ final class AutoFillManagerServiceImpl { return; } - final Session session = getSessionByTokenLocked(activityToken); + final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags + + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds; + mRequestsHistory.log(historyItem); - if (session != null) { - // Session already exist, update UI instead... - /* - * TODO(b/33197203): currently, it's always reusing the session, regardless of the - * requested autoFillId, but it should start a new session for views that - * were not part of the initial auto-fill dataset returned by the service. For example: - * - * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`. - * 2.User taps `first_name`. - * 3.Service returns a dataset with ids for `first_name` and `last_name`. - * 4.When user taps `first_name` (again) or `last_name`, session should be reused, but - * when user taps `address`, it should start a new session (since that field was - * not part of the initial dataset). - * - * Similarly, once the activity is auto-filled, the flag logic should be reset (so if - * the user taps the view again, a new auto-fill request is made) - */ - if (DEBUG) { - Slog.d(TAG, "requestAutoFillLocked(): reusing session for token " - + activityToken + ", id " + autoFillId + " and flags " + flags); - } + // TODO(b/33197203): Handle partitioning + Session session = getOrCreateSessionByTokenLocked(activityToken); + if (DEBUG) Slog.d(TAG, "using Session: " + session.mId); - if ((flags & FLAG_UPDATE_UI_HIDE) != 0) { - // TODO(b/33197203): handle it? - if (DEBUG) Slog.d(TAG, "ignoring FLAG_UPDATE_UI_HIDE request for " + autoFillId); + session.updateAutoFillInput(flags, autoFillId, null, bounds); + } - return; + private Session getOrCreateSessionByTokenLocked(IBinder activityToken) { + final int size = mSessions.size(); + for (int i = 0; i < size; i++) { + final Session session = mSessions.valueAt(i); + if (activityToken.equals(session.mActivityToken.get())) { + return session; } - - session.mCurrentAutoFillId = autoFillId; - session.mCurrentBounds = bounds; - mUi.showResponse(mUserId, session.mId, autoFillId, bounds, session.mCurrentResponse); - return; } + return createSessionByTokenLocked(activityToken); + } + private Session createSessionByTokenLocked(IBinder activityToken) { final int sessionId = ++sSessionIdCounter; - if (DEBUG) { - Slog.d(TAG, "requestAutoFillLocked(): new session (id=" + sessionId + " for token " - + activityToken + " and autoFillId " + autoFillId); - } + if (DEBUG) Slog.d(TAG, "creating Session: " + sessionId); - final Session newSession = new Session(sessionId, activityToken, autoFillId, bounds); + final Session newSession = new Session(sessionId, activityToken); mSessions.put(sessionId, newSession); - final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags - + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds; - mRequestsHistory.log(historyItem); - /* * TODO(b/33197203): apply security checks below: * - checks if disabled by secure settings / device policy @@ -353,7 +338,8 @@ final class AutoFillManagerServiceImpl { */ try { // TODO(b/33197203): add MetricsLogger call - if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, flags)) { + if (!mAm.requestAutoFillData( + mAssistReceiver, null, sessionId, activityToken, AUTO_FILL_FLAG_TYPE_FILL)) { // TODO(b/33197203): might need a way to warn user (perhaps a new method on // AutoFillService). Slog.w(TAG, "failed to request auto-fill data for " + activityToken); @@ -361,75 +347,7 @@ final class AutoFillManagerServiceImpl { } catch (RemoteException e) { // Should not happen, it's a local call. } - } - - /** - * Called by UI to trigger a save request to the service. - */ - void requestSaveLocked(int sessionId) { - // TODO(b/33197203): add MetricsLogger call - // TODO(b/33197203): use handler? - // TODO(b/33197203): show error on UI on Slog.w situations below??? - - if (mService == null) { - Slog.w(TAG, "requestSave(): service is null"); - return; - } - final Session session = mSessions.get(sessionId); - if (session == null) { - Slog.w(TAG, "requestSave(): no session with id " + sessionId); - return; - } - final IBinder activityToken = session.mActivityToken.get(); - if (activityToken == null) { - Slog.w(TAG, "activity token for session " + sessionId + " already GCed"); - return; - } - - /* - * TODO(b/33197203): apply security checks below: - * - checks if disabled by secure settings / device policy - * - log operation using noteOp() - * - check flags - * - display disclosure if needed - */ - try { - /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and get - the extras to be sent to the service based on the response / dataset in the session. - Something like: - final Bundle extras = (responseExtras == null && datasetExtras == null) - ? null : new Bundle(); - if (responseExtras != null) { - if (DEBUG) Slog.d(TAG, "response extras on save notification: " + - bundleToString(responseExtras)); - extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras); - } - if (datasetExtras != null) { - if (DEBUG) Slog.d(TAG, "dataset extras on save notification: " + - bundleToString(datasetExtras)); - extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras); - } - - */ - - if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, - AUTO_FILL_FLAG_TYPE_SAVE)) { - Slog.w(TAG, "failed to save for " + activityToken); - } - } catch (RemoteException e) { - // Should not happen, it's a local call. - } - } - - private Session getSessionByTokenLocked(IBinder activityToken) { - final int size = mSessions.size(); - for (int i = 0; i < size; i++) { - final Session session = mSessions.valueAt(i); - if (activityToken.equals(session.mActivityToken.get())) { - return session; - } - } - return null; + return newSession; } void stopLocked() { @@ -459,35 +377,6 @@ final class AutoFillManagerServiceImpl { } } - /** - * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset. - */ - void autoFillApp(int sessionId, Dataset dataset) { - // TODO(b/33197203): add MetricsLogger call - - if (dataset == null) { - Slog.w(TAG, "autoFillApp(): no dataset for callback id " + sessionId); - return; - } - - - final Session session; - synchronized (mLock) { - session = mSessions.get(sessionId); - if (session == null) { - Slog.w(TAG, "autoFillApp(): no session with id " + sessionId); - return; - } - if (session.mAppCallback == null) { - Slog.w(TAG, "autoFillApp(): no app callback for session " + sessionId); - return; - } - - // TODO(b/33197203): use a handler? - session.autoFill(dataset); - } - } - void removeSessionLocked(int id) { if (DEBUG) Slog.d(TAG, "Removing session " + id); mSessions.remove(id); @@ -495,43 +384,6 @@ final class AutoFillManagerServiceImpl { // TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback? } - /** - * 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); - } - } - } - void dumpLocked(String prefix, PrintWriter pw) { if (!mValid) { pw.print(" NOT VALID: "); @@ -612,8 +464,78 @@ final class AutoFillManagerServiceImpl { } /** - * A bridge between the {@link AutoFillService} implementation and the activity being - * auto-filled (represented through the {@link IAutoFillAppCallback}). + * State for a given view with a AutoFillId. + * + * <p>This class holds state about a view and calls its listener when the fill UI is ready to + * be displayed for the view. + */ + static final class ViewState { + interface Listener { + /** + * Called when the fill UI is ready to be shown for this view. + */ + void onFillReady(ViewState viewState, FillResponse fillResponse, Rect bounds, + @Nullable AutoFillValue value); + } + + private final Listener mListener; + @Nullable + private FillResponse mResponse; + private AutoFillValue mAutoFillValue; + private Rect mBounds; + + ViewState(Listener listener) { + mListener = listener; + } + + /** + * 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(); + } + + void update(@Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) { + if (autoFillValue != null) { + mAutoFillValue = autoFillValue; + } + if (bounds != null) { + mBounds = bounds; + } + + maybeCallOnFillReady(); + } + + /** + * Calls {@link Listener#onFillReady(ViewState, FillResponse, Rect, AutoFillValue)} if the + * fill UI is ready to be displayed (i.e. when response and bounds are set). + */ + void maybeCallOnFillReady() { + if (mResponse != null && mBounds != null) { + mListener.onFillReady(this, mResponse, mBounds, mAutoFillValue); + } + } + + @Override + public String toString() { + if (!DEBUG) return super.toString(); + + return "ViewState: [response=" + mResponse + ", value=" + mAutoFillValue + + ", bounds=" + mBounds + "]"; + } + } + + /** + * A session for a given activity. + * + * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track + * of the current view session to display the appropriate UI. * * <p>Although the auto-fill requests and callbacks are stateless from the service's point of * view, we need to keep state in the framework side for cases such as authentication. For @@ -625,23 +547,23 @@ final class AutoFillManagerServiceImpl { // - On all authentication scenarios. // - When user does not interact back after a while. // - When service is unbound. - private final class Session { + final class Session implements ViewState.Listener { - private final int mId; + private final AutoFillUI mUi; + final int mId; private final WeakReference<IBinder> mActivityToken; - private IAutoFillAppCallback mAppCallback; - - // Current view where the auto-fill bar is displayed @GuardedBy("mLock") - private AutoFillId mCurrentAutoFillId; + private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>(); @GuardedBy("mLock") - private Rect mCurrentBounds; - @GuardedBy("mLock") - private FillResponse mCurrentResponse; + @Nullable + private ViewState mCurrentViewState; - private final IFingerprintService mFingerprintService; + private IAutoFillAppCallback mAppCallback; + // TODO(b/33197203): Get a response per view instead of per activity. + @GuardedBy("mLock") + private FillResponse mCurrentResponse; @GuardedBy("mLock") private FillResponse mResponseRequiringAuth; @GuardedBy("mLock") @@ -683,7 +605,7 @@ final class AutoFillManagerServiceImpl { notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(), FLAG_AUTHENTICATION_SUCCESS); } else { - autoFillAppLocked(mDatasetRequiringAuth, true); + autoFillApp(mDatasetRequiringAuth); } } else if (mResponseRequiringAuth != null) { final List<Dataset> datasets = mResponseRequiringAuth.getDatasets(); @@ -697,7 +619,7 @@ final class AutoFillManagerServiceImpl { Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset"); } - mUi.dismissFingerprintRequest(mUserId, true); + mUi.dismissFingerprintRequest(true); } @Override @@ -721,7 +643,7 @@ final class AutoFillManagerServiceImpl { Slog.w(TAG, "onError(): no response or dataset"); } - mUi.dismissFingerprintRequest(mUserId, false); + mUi.dismissFingerprintRequest(false); } @Override @@ -741,7 +663,6 @@ final class AutoFillManagerServiceImpl { // TODO(b/33197203): add MetricsLogger call if (response == null) { if (DEBUG) Slog.d(TAG, "showResponse(): null response"); - removeSelf(); return; } @@ -805,20 +726,16 @@ final class AutoFillManagerServiceImpl { if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags); if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) { - autoFillAppLocked(dataset != null ? dataset : mDatasetRequiringAuth, true); + autoFillApp(dataset != null ? dataset : mDatasetRequiringAuth); return; } - removeSelf(); } }; - private Session(int id, IBinder activityToken, AutoFillId autoFillId, Rect bounds) { - this.mId = id; - this.mActivityToken = new WeakReference<>(activityToken); - this.mCurrentAutoFillId = autoFillId; - this.mCurrentBounds = bounds; - this.mFingerprintService = IFingerprintService.Stub - .asInterface(ServiceManager.getService("fingerprint")); + private Session(int id, IBinder activityToken) { + mUi = new AutoFillUI(mContext, this); + mId = id; + mActivityToken = new WeakReference<>(activityToken); } void setAppCallback(IBinder appBinder) { @@ -834,6 +751,54 @@ final class AutoFillManagerServiceImpl { mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder); } + void updateAutoFillInput(int flags, AutoFillId autoFillId, + @Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) { + synchronized (mLock) { + ViewState viewState = mViewStates.get(autoFillId); + if (viewState == null) { + viewState = new ViewState(this); + mViewStates.put(autoFillId, viewState); + } + + if ((flags & FLAG_UPDATE_UI_SHOW) != 0) { + // Remove the UI if the ViewState has changed. + if (mCurrentViewState != viewState) { + mUi.hideFillUi(); + mCurrentViewState = viewState; + } + + // If the ViewState is ready to be displayed, onReady() will be called. + viewState.update(autoFillValue, bounds); + + // TODO(b/33197203): Remove when there is a response per activity. + if (mCurrentResponse != null) { + viewState.setResponse(mCurrentResponse); + } + } else if ((flags & FLAG_UPDATE_UI_HIDE) != 0) { + if (mCurrentViewState == viewState) { + mUi.hideFillUi(); + mCurrentViewState = null; + } + } else { + Slog.w(TAG, "unknown flags " + flags); + } + } + } + + @Override + public void onFillReady(ViewState viewState, FillResponse response, Rect bounds, + @Nullable AutoFillValue value) { + String filterText = ""; + if (value != null) { + // TODO(b/33197203): Handle other AutoFillValue types + final CharSequence text = value.getTextValue(); + if (text != null) { + filterText = text.toString(); + } + } + mUi.showFillUi(viewState, response.getDatasets(), bounds, filterText); + } + private void showResponseLocked(FillResponse response, boolean authRequired) { if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly + ", authRequired=" + authRequired +"):" + response); @@ -845,7 +810,7 @@ final class AutoFillManagerServiceImpl { final Dataset dataset = datasets.get(0); if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset); - autoFillAppLocked(dataset, true); + autoFillApp(dataset); return; } } @@ -853,7 +818,10 @@ final class AutoFillManagerServiceImpl { if (!authRequired) { // TODO(b/33197203): add MetricsLogger call mCurrentResponse = response; - mUi.showResponse(mUserId, mId, mCurrentAutoFillId, mCurrentBounds, mCurrentResponse); + // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design + if (mCurrentViewState != null) { + mCurrentViewState.setResponse(mCurrentResponse); + } return; } @@ -869,7 +837,7 @@ final class AutoFillManagerServiceImpl { scanFingerprint(response.getCryptoObjectOpId()); } // Displays the message asking the user to tap (or fingerprint) for AutoFill. - mUi.showFillResponseAuthenticationRequest(mUserId, mId, requiresFingerprint, + mUi.showFillResponseAuthenticationRequest(requiresFingerprint, response.getExtras(), response.getFlags()); } @@ -877,7 +845,7 @@ final class AutoFillManagerServiceImpl { synchronized (mLock) { // Autofill it directly... if (!dataset.isAuthRequired()) { - autoFillAppLocked(dataset, true); + autoFillApp(dataset); return; } @@ -906,30 +874,114 @@ final class AutoFillManagerServiceImpl { void dumpLocked(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("mId: "); pw.println(mId); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get()); - pw.print(prefix); pw.print("mCurrentAutoFillId: "); pw.println(mCurrentAutoFillId); - pw.print(prefix); pw.print("mCurrentBounds: "); pw.println(mCurrentBounds); 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 + " "; + for (Map.Entry<AutoFillId, ViewState> entry : mViewStates.entrySet()) { + pw.print(prefix2); + pw.print(entry.getKey()); pw.print(": " ); pw.println(entry.getValue()); + } } - private void autoFillAppLocked(Dataset dataset, boolean removeSelf) { - try { - if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); - mAppCallback.autoFill(dataset); + /** + * 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); + } + } + } - // TODO(b/33197203): temporarily hack: show the save notification after autofilled, - // since save is not automatically detected yet. - mUi.showSaveNotification(mUserId, mId); removeSelf = false; + /** + * 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); + } + } + } - } catch (RemoteException e) { - Slog.w(TAG, "Error auto-filling activity: " + e); + void autoFillApp(Dataset dataset) { + synchronized (mLock) { + try { + if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); + mAppCallback.autoFill(dataset); + } catch (RemoteException e) { + Slog.w(TAG, "Error auto-filling activity: " + e); + } } - if (removeSelf) { - removeSelf(); + } + + void requestSave() { + synchronized (mLock) { + requestSaveLocked(mId); + } + } + + /** + * Called by UI to trigger a save request to the service. + */ + void requestSaveLocked(int sessionId) { + // TODO(b/33197203): add MetricsLogger call + // TODO(b/33197203): use handler? + // TODO(b/33197203): show error on UI on Slog.w situations below??? + + if (mService == null) { + Slog.w(TAG, "requestSave(): service is null"); + return; + } + final Session session = mSessions.get(sessionId); + if (session == null) { + Slog.w(TAG, "requestSave(): no session with id " + sessionId); + return; + } + final IBinder activityToken = session.mActivityToken.get(); + if (activityToken == null) { + Slog.w(TAG, "activity token for session " + sessionId + " already GCed"); + return; + } + + /* + * TODO(b/33197203): apply security checks below: + * - checks if disabled by secure settings / device policy + * - log operation using noteOp() + * - check flags + * - display disclosure if needed + */ + try { + /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and + get the extras to be sent to the service based on the response / dataset in the + session. */ + if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, + AUTO_FILL_FLAG_TYPE_SAVE)) { + Slog.w(TAG, "failed to save for " + activityToken); + } + } catch (RemoteException e) { + // Should not happen, it's a local call. } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java index 96f340840542..86e04ccd6a83 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java @@ -45,6 +45,8 @@ 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; @@ -59,24 +61,25 @@ final class AutoFillUI { private static final String TAG = "AutoFillUI"; private final Context mContext; - + private final Session mSession; private final WindowManager mWm; - @Nullable + // Fill UI variables private AnchoredWindow mFillWindow; + private DatasetPicker mFillView; + private ViewState mViewState; + private Rect mBounds; + private String mFilterText; /** * Custom snackbar UI used for saving autofill or other informational messages. */ private View mSnackbar; - AutoFillUI(Context context, AutoFillManagerService service, Object lock) { + AutoFillUI(Context context, Session session) { mContext = context; + mSession = session; mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mService = service; - mLock = lock; - - setNotificationListener(); } /** @@ -101,29 +104,63 @@ final class AutoFillUI { } /** - * 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}). + * Hides the fill UI. */ - void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds, - FillResponse response) { - if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId + ", bounds=" + bounds); - + void hideFillUi() { UiThread.getHandler().runWithScissors(() -> { if (mFillWindow != null) { + if (DEBUG) Slog.d(TAG, "remove FillUi remove " + mFillWindow); mFillWindow.hide(); } - final DatasetPicker fillView = new DatasetPicker(mContext, response.getDatasets(), - (dataset) -> { - mFillWindow.hide(); - onDatasetPicked(userId, dataset, sessionId); - }); + mViewState = null; + mBounds = null; + mFilterText = null; + mFillView = null; + mFillWindow = null; + }, 0); + } + + /** + * Shows the fill UI, removing the previous fill UI if the has changed. + * + * @param viewState the view state, compared by reference to know if new UI should be shown + * @param response the response to show, not used if viewState is the same + * @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, + String filterText) { + UiThread.getHandler().runWithScissors(() -> { + if (mViewState != viewState) { + // new + hideFillUi(); + + mViewState = viewState; + + mFillView = new DatasetPicker(mContext, datasets, + (dataset) -> { + mSession.autoFillApp(dataset); + hideFillUi(); + showSaveUi(); + }); + 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; + mFillWindow.show(mBounds); + } - // TODO(b/33197203): request width/height properly. - mFillWindow = new AnchoredWindow(mWm, fillView, 800, - ViewGroup.LayoutParams.WRAP_CONTENT); - mFillWindow.show(bounds != null ? bounds : new Rect()); + if (!filterText.equals(mFilterText)) { + if (DEBUG) Slog.d(TAG, "update FillUi filter text: " + mFilterText); + mFilterText = filterText; + mFillView.update(mFilterText); + } }, 0); } @@ -134,10 +171,10 @@ final class AutoFillUI { * <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(int userId, int sessionId, boolean usesFingerprint, + void showFillResponseAuthenticationRequest(boolean usesFingerprint, Bundle extras, int flags) { // TODO(b/33197203): proper implementation - showAuthNotification(userId, sessionId, usesFingerprint, extras, flags); + showAuthNotification(usesFingerprint, extras, flags); } /** @@ -161,15 +198,13 @@ final class AutoFillUI { /** * Shows the UI asking the user to save for auto-fill. */ - void showSaveUI(int userId, int sessionId) { + void showSaveUi() { showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() { @Override public void onSaveClick() { hideSnackbar(); - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceLocked(userId); - service.requestSaveLocked(sessionId); - } + + mSession.requestSave(); } @Override public void onCancelClick() { @@ -181,10 +216,10 @@ final class AutoFillUI { /** * Called by service after the user user the fingerprint sensors to authenticate. */ - void dismissFingerprintRequest(int userId, boolean success) { + void dismissFingerprintRequest(boolean success) { if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success); - dismissAuthNotification(userId); + dismissAuthNotification(); if (!success) { // TODO(b/33197203): proper implementation (snack bar / i18n string) @@ -198,48 +233,11 @@ final class AutoFillUI { 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("mSnackBar: "); pw.println(mSnackbar); - mFillWindow.dump(pw); - } - - private AutoFillManagerServiceImpl getServiceLocked(int userId) { - final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId); - if (service == null) { - Slog.w(TAG, "no auto-fill service for user " + userId); - } - return service; - } - - private void onSaveRequested(int userId, int sessionId) { - // TODO(b/33197203): displays the snack bar, until save notification is refactored - showSaveUI(userId, sessionId); - } - - private void onDatasetPicked(int userId, Dataset dataset, int sessionId) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceLocked(userId); - if (service == null) return; - - service.autoFillApp(sessionId, dataset); - } - } - - private void onSessionDone(int userId, int sessionId) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceLocked(userId); - if (service == null) return; - - service.removeSessionLocked(sessionId); - } - } - - private void onResponseAuthenticationRequested(int userId, Bundle extras, int flags) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceLocked(userId); - if (service == null) return; - - service.notifyResponseAuthenticationResult(extras, flags); - } + pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState); + pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds); + pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText); } //similar to a snackbar, but can be a bit custom since it is more than just text. This will @@ -289,16 +287,10 @@ final class AutoFillUI { private static final String EXTRA_FLAGS = "flags"; private static final String TYPE_OPTIONS = "options"; - private static final String TYPE_FINISH_SESSION = "finish_session"; - private static final String TYPE_PICK_DATASET = "pick_dataset"; - private static final String TYPE_SAVE = "save"; private static final String TYPE_AUTH_RESPONSE = "auth_response"; - @GuardedBy("mServiceLock") private BroadcastReceiver mNotificationReceiver; - @GuardedBy("mServiceLock") - private final AutoFillManagerService mService; - private final Object mLock; + private final Object mLock = new Object(); // Hack used to generate unique pending intents static int sResultCode = 0; @@ -316,8 +308,6 @@ final class AutoFillUI { final class NotificationReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(EXTRA_USER_ID, -1); - final int sessionId = intent.getIntExtra(EXTRA_SESSION_ID, -1); final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE); if (type == null) { Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent); @@ -326,26 +316,12 @@ final class AutoFillUI { final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET); final int flags = intent.getIntExtra(EXTRA_FLAGS, 0); - if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId - + ", sessionId=" + sessionId); + if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + + ", sessionId=" + mSession.mId); synchronized (mLock) { switch (type) { - case TYPE_SAVE: - onSaveRequested(userId, sessionId); - break; - case TYPE_FINISH_SESSION: - onSessionDone(userId, sessionId); - break; - case TYPE_PICK_DATASET: - onDatasetPicked(userId, dataset, sessionId); - - // Must cancel notification because it might be comming from action - if (DEBUG) Slog.d(TAG, "Cancelling notification"); - NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId); - - break; case TYPE_AUTH_RESPONSE: - onResponseAuthenticationRequested(userId, + mSession.notifyResponseAuthenticationResult( intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags); break; default: { @@ -357,161 +333,28 @@ final class AutoFillUI { } } - private static Intent newNotificationIntent(int userId, String type) { + private static Intent newNotificationIntent(String type) { final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT); - intent.putExtra(EXTRA_USER_ID, userId); intent.putExtra(EXTRA_NOTIFICATION_TYPE, type); return intent; } - private PendingIntent newPickDatasetPI(int userId, int sessionId, FillResponse response, - Dataset dataset) { - final int resultCode = ++ sResultCode; - if (DEBUG) Slog.d(TAG, "newPickDatasetPI: userId=" + userId + ", sessionId=" + sessionId - + ", resultCode=" + resultCode); - - final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET); - intent.putExtra(EXTRA_SESSION_ID, sessionId); - intent.putExtra(EXTRA_FILL_RESPONSE, response); - intent.putExtra(EXTRA_DATASET, dataset); - return PendingIntent.getBroadcast(mContext, resultCode, intent, - PendingIntent.FLAG_ONE_SHOT); - } - - /** - * Shows a notification with the results of an auto-fill request, using notications actions - * to emulate the auto-fill bar buttons displaying the dataset names. - */ - private void showOptionsNotification(int userId, int callbackId, AutoFillId autoFillId, - FillResponse response) { - final long token = Binder.clearCallingIdentity(); - try { - showOptionsNotificationAsSystem(userId, callbackId, autoFillId, response); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void showOptionsNotificationAsSystem(int userId, int sessionId, - AutoFillId autoFillId, FillResponse response) { - // Make sure server callback is removed from cache if user cancels the notification. - final Intent deleteIntent = newNotificationIntent(userId, TYPE_FINISH_SESSION) - .putExtra(EXTRA_SESSION_ID, sessionId); - final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext, - ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT); - - final String title = "AutoFill Options"; - - final Notification.Builder notification = newNotificationBuilder() - .setOngoing(false) - .setDeleteIntent(deletePendingIntent) - .setContentTitle(title); - - boolean autoCancel = true; - final String subTitle; - final List<Dataset> datasets; - final AutoFillId[] savableIds; - if (response != null) { - datasets = response.getDatasets(); - savableIds = response.getSavableIds(); - } else { - datasets = null; - savableIds = null; - } - boolean showSave = false; - if (datasets == null ) { - subTitle = "No options to auto-fill " + autoFillId; - } else if (datasets.isEmpty()) { - if (savableIds.length == 0) { - subTitle = "No options to auto-fill " + autoFillId; - } else { - subTitle = "No options to auto-fill " + autoFillId - + ", but provider can save ids:\n" + Arrays.toString(savableIds); - showSave = true; - } - } else { - final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId); - if (service == null) { - subTitle = "No auto-fill service for user " + userId; - Slog.w(TAG, subTitle); - } else { - autoCancel = false; - final int size = datasets.size(); - subTitle = "There are " + size + " option(s) to fill " + autoFillId + ".\n" - + "Use the notification action(s) to select the proper one." - + "Actions with (F) require fingerprint unlock, and with (P) require" - + "provider authentication to unlock"; - for (Dataset dataset : datasets) { - final StringBuilder name = new StringBuilder(dataset.getName()); - if (dataset.isAuthRequired()) { - if (dataset.hasCryptoObject()) { - name.append("(F)"); - } else { - name.append("(P)"); - } - } - final PendingIntent pi = newPickDatasetPI(userId, sessionId, response, dataset); - notification.addAction(new Action.Builder(null, name, pi).build()); - } - } - } - - notification.setAutoCancel(autoCancel); - notification.setStyle(new Notification.BigTextStyle().bigText(subTitle)); - - NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build()); - - if (showSave) { - showSaveNotification(userId, sessionId); - } - } - - void showSaveNotification(int userId, int sessionId) { - final long token = Binder.clearCallingIdentity(); - try { - showSaveNotificationAsSystem(userId, sessionId); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void showSaveNotificationAsSystem(int userId, int sessionId) { - final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE) - .putExtra(EXTRA_SESSION_ID, sessionId); - - final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext, - ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT); - - final String title = "AutoFill Save Emulation"; - final String subTitle = "Tap notification to launch the save snackbar."; - - final Notification notification = newNotificationBuilder() - .setAutoCancel(true) - .setOngoing(false) - .setContentTitle(title) - .setContentIntent(savePendingIntent) - .setStyle(new Notification.BigTextStyle().bigText(subTitle)) - .build(); - NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification); - } - - private void showAuthNotification(int userId, int sessionId, boolean usesFingerprint, + private void showAuthNotification(boolean usesFingerprint, Bundle extras, int flags) { final long token = Binder.clearCallingIdentity(); try { - showAuthNotificationAsSystem(userId, sessionId, usesFingerprint, extras, flags); + showAuthNotificationAsSystem(usesFingerprint, extras, flags); } finally { Binder.restoreCallingIdentity(token); } } - private void showAuthNotificationAsSystem(int userId, int sessionId, + private void showAuthNotificationAsSystem( boolean usesFingerprint, Bundle extras, int flags) { final String title = "AutoFill Authentication"; final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n"); - final Intent authIntent = newNotificationIntent(userId, TYPE_AUTH_RESPONSE) - .putExtra(EXTRA_SESSION_ID, sessionId); + final Intent authIntent = newNotificationIntent(TYPE_AUTH_RESPONSE); if (extras != null) { authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras); } @@ -537,11 +380,11 @@ final class AutoFillUI { if (authPendingIntent != null) { notification.setContentIntent(authPendingIntent); } - NotificationManager.from(mContext).notify(TYPE_AUTH_RESPONSE, userId, notification.build()); + NotificationManager.from(mContext).notify(mSession.mId, notification.build()); } - private void dismissAuthNotification(int userId) { - NotificationManager.from(mContext).cancel(TYPE_AUTH_RESPONSE, userId); + private void dismissAuthNotification() { + NotificationManager.from(mContext).cancel(mSession.mId); } private Notification.Builder newNotificationBuilder() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b82999ec99b1..a83597672cc9 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4559,18 +4559,18 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyEnqueued(info, sbnToPost, importance, fromUser); + notifyEnqueued(info, sbnToPost); } }); } } private void notifyEnqueued(final ManagedServiceInfo info, - final StatusBarNotification sbn, int importance, boolean fromUser) { + final StatusBarNotification sbn) { final INotificationListener assistant = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { - assistant.onNotificationEnqueued(sbnHolder, importance, fromUser); + assistant.onNotificationEnqueued(sbnHolder); } catch (RemoteException ex) { Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex); } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 829c47363bb8..42934a43fb25 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -469,7 +469,7 @@ class InstantAppRegistry { return; } final int appCount = uninstalledAppStates.size(); - for (int i = 0; i < appCount; i++) { + for (int i = appCount - 1; i >= 0; --i) { UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i); if (!criteria.test(uninstalledAppState)) { continue; diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 915951381ac7..3eb529b568f2 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1131,8 +1131,10 @@ public class AppTransition implements Dump { // We scale the width and clip to the top/left square float scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right); - int unscaledThumbHeight = (int) (thumbHeight / scale); - mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight; + if (!mGridLayoutRecentsEnabled) { + int unscaledThumbHeight = (int) (thumbHeight / scale); + mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight; + } mNextAppTransitionInsets.set(contentInsets); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d62c62e7b328..9b96523af73f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2171,7 +2171,8 @@ public class WindowManagerService extends IWindowManager.Stub if (!win.mHasSurface) { result |= RELAYOUT_RES_SURFACE_CHANGED; } - WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(); + WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked( + win.mAttrs.type, win.mOwnerUid); if (surfaceController != null) { surfaceController.getSurface(outSurface); if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied"); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 19ef44cace33..abce222bf33f 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -577,7 +577,7 @@ class WindowStateAnimator { } } - WindowSurfaceController createSurfaceLocked() { + WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) { final WindowState w = mWin; if (w.restoreSavedSurface()) { if (DEBUG_ANIM) Slog.i(TAG, @@ -653,7 +653,7 @@ class WindowStateAnimator { mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession, attrs.getTitle().toString(), - width, height, format, flags, this); + width, height, format, flags, this, windowType, ownerUid); w.setHasSurface(true); @@ -1321,8 +1321,8 @@ class WindowStateAnimator { } // We want to calculate the scaling based on the content area, not based on // the entire surface, so that we scale in sync with windows that don't have insets. - mExtraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets); - mExtraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets); + mExtraHScale = (finalClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets); + mExtraVScale = (finalClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets); // In the case of ForceScaleToCrop we scale entire tasks together, // and so we need to scale our offsets relative to the task bounds @@ -1345,8 +1345,7 @@ class WindowStateAnimator { // Since we are scaled to fit in our previously desired crop, we can now // expose the whole window in buffer space, and not risk extending // past where the system would have cropped us - mTmpClipRect.set(0, 0, (int)surfaceWidth, (int)surfaceHeight); - mTmpFinalClipRect.setEmpty(); + clipRect = null; // Various surfaces in the scaled stack may resize at different times. // We need to ensure for each surface, that we disable transformation matrix diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index c48a58514c3d..1096ede96316 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -79,8 +79,8 @@ class WindowSurfaceController { private final WindowManagerService mService; - public WindowSurfaceController(SurfaceSession s, - String name, int w, int h, int format, int flags, WindowStateAnimator animator) { + public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format, + int flags, WindowStateAnimator animator, int windowType, int ownerUid) { mAnimator = animator; mSurfaceW = w; @@ -98,13 +98,13 @@ class WindowSurfaceController { animator.mWin.mSubLayer < 0 && animator.mWin.mAppToken != null) { mSurfaceControl = new SurfaceControlWithBackground(s, - name, w, h, format, flags, animator.mWin.mAppToken); + name, w, h, format, flags, animator.mWin.mAppToken, windowType, ownerUid); } else if (DEBUG_SURFACE_TRACE) { mSurfaceControl = new SurfaceTrace( - s, name, w, h, format, flags); + s, name, w, h, format, flags, windowType, ownerUid); } else { mSurfaceControl = new SurfaceControl( - s, name, w, h, format, flags); + s, name, w, h, format, flags, windowType, ownerUid); } if (mService.mRoot.mSurfaceTraceEnabled) { @@ -569,9 +569,21 @@ class WindowSurfaceController { private float mDsdx, mDtdx, mDsdy, mDtdy; private final String mName; + public SurfaceTrace(SurfaceSession s, String name, int w, int h, int format, int flags, + int windowType, int ownerUid) + throws OutOfResourcesException { + super(s, name, w, h, format, flags, windowType, ownerUid); + mName = name != null ? name : "Not named"; + mSize.set(w, h); + if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by " + + Debug.getCallers(3)); + synchronized (sSurfaces) { + sSurfaces.add(0, this); + } + } + public SurfaceTrace(SurfaceSession s, - String name, int w, int h, int format, int flags) - throws OutOfResourcesException { + String name, int w, int h, int format, int flags) { super(s, name, w, h, format, flags); mName = name != null ? name : "Not named"; mSize.set(w, h); @@ -806,11 +818,10 @@ class WindowSurfaceController { public boolean mVisible = false; public int mLayer = -1; - public SurfaceControlWithBackground(SurfaceSession s, - String name, int w, int h, int format, int flags, - AppWindowToken token) - throws OutOfResourcesException { - super(s, name, w, h, format, flags); + public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format, + int flags, AppWindowToken token, int windowType, int ownerUid) + throws OutOfResourcesException { + super(s, name, w, h, format, flags, windowType, ownerUid); mBackgroundControl = new SurfaceControl(s, name, w, h, PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM); mOpaque = (flags & SurfaceControl.OPAQUE) != 0; diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml index 07f7617828e5..c20be5141df5 100644 --- a/tests/UiBench/AndroidManifest.xml +++ b/tests/UiBench/AndroidManifest.xml @@ -100,6 +100,22 @@ </intent-filter> </activity> <activity + android:name=".FadingEdgeListActivity" + android:label="General/Fading Edge ListView" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity> + <activity + android:name=".SaveLayerInterleaveActivity" + android:label="General/SaveLayer Animation" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.uibench.TEST" /> + </intent-filter> + </activity> + <activity android:name=".ClippedListActivity" android:label="General/Clipped ListView" android:theme="@style/NoActionBar"> diff --git a/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java b/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java new file mode 100644 index 000000000000..3241e669fb1d --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java @@ -0,0 +1,37 @@ +/* + * 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.test.uibench; + +import android.support.v4.app.ListFragment; +import android.widget.ArrayAdapter; +import android.widget.ListAdapter; + +import com.android.test.uibench.listview.CompatListActivity; +import com.android.test.uibench.listview.FadingEdgeListFragment; + +public class FadingEdgeListActivity extends CompatListActivity { + + @Override + protected ListAdapter createListAdapter() { + return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + TextUtils.buildSimpleStringList(40)); + } + + @Override + protected ListFragment createListFragment() { + return (ListFragment)new FadingEdgeListFragment(); + } +} diff --git a/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java b/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java new file mode 100644 index 000000000000..eec91cb38066 --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java @@ -0,0 +1,89 @@ +/* + * 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.test.uibench; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +/** + * Test Canvas.saveLayer performance by interleaving drawText/drawRect with saveLayer. + * This test will be used to measure if drawing interleaved layers at the beginning of a frame will + * decrease FBO switching overhead (this is a future optimization in SkiaGL rendering pipeline). + */ +public class SaveLayerInterleaveActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setBackgroundDrawable(new Drawable() { + private final Paint mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + @Override + public void setAlpha(int alpha) { + } + + @Override + public int getOpacity() { + return PixelFormat.OPAQUE; + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + } + + @Override + public void draw(Canvas canvas) { + canvas.drawColor(Color.RED); + + Rect bounds = getBounds(); + int regions = 20; + int smallRectHeight = (bounds.height()/regions); + int padding = smallRectHeight / 4; + int top = bounds.top; + mBluePaint.setColor(Color.BLUE); + mBluePaint.setTextSize(padding); + mGreenPaint.setColor(Color.GREEN); + mGreenPaint.setTextSize(padding); + + //interleave drawText and drawRect with saveLayer ops + for (int i = 0; i < regions; i++, top += smallRectHeight) { + canvas.saveLayer(bounds.left, top, bounds.right, top + padding, + mBluePaint); + canvas.drawColor(Color.YELLOW); + canvas.drawText("offscreen line "+ i, bounds.left, top + padding, + mBluePaint); + canvas.restore(); + + Rect partX = new Rect(bounds.left, top + padding, + bounds.right,top + smallRectHeight - padding); + canvas.drawRect(partX, mBluePaint); + canvas.drawText("onscreen line "+ i, bounds.left, + top + smallRectHeight - padding, mGreenPaint); + } + + invalidateSelf(); + } + }); + } +} diff --git a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java index 214c07463fd7..bb7f4a302f8c 100644 --- a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java @@ -29,11 +29,15 @@ public abstract class CompatListActivity extends AppCompatActivity { FragmentManager fm = getSupportFragmentManager(); if (fm.findFragmentById(android.R.id.content) == null) { - ListFragment listFragment = new ListFragment(); + ListFragment listFragment = createListFragment(); listFragment.setListAdapter(createListAdapter()); fm.beginTransaction().add(android.R.id.content, listFragment).commit(); } } protected abstract ListAdapter createListAdapter(); + + protected ListFragment createListFragment() { + return new ListFragment(); + } } diff --git a/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java b/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java new file mode 100644 index 000000000000..a018b40e8528 --- /dev/null +++ b/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java @@ -0,0 +1,31 @@ +/* + * 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.test.uibench.listview; + +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.widget.ListView; + +public class FadingEdgeListFragment extends ListFragment { + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + ListView listView = getListView(); + listView.setVerticalFadingEdgeEnabled(true); + listView.setFadingEdgeLength(500); + super.onActivityCreated(savedInstanceState); + } +} |