diff options
28 files changed, 953 insertions, 79 deletions
diff --git a/api/current.txt b/api/current.txt index 09503eea8cf9..1b331ac3f07d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -44260,6 +44260,7 @@ package android.view.accessibility { public final class AccessibilityWindowInfo implements android.os.Parcelable { method public int describeContents(); + method public android.view.accessibility.AccessibilityNodeInfo getAnchor(); method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityWindowInfo getChild(int); method public int getChildCount(); @@ -44267,6 +44268,7 @@ package android.view.accessibility { method public int getLayer(); method public android.view.accessibility.AccessibilityWindowInfo getParent(); method public android.view.accessibility.AccessibilityNodeInfo getRoot(); + method public java.lang.CharSequence getTitle(); method public int getType(); method public boolean isAccessibilityFocused(); method public boolean isActive(); diff --git a/api/system-current.txt b/api/system-current.txt index 7e91505a32a6..867675c337ee 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47003,6 +47003,7 @@ package android.view.accessibility { public final class AccessibilityWindowInfo implements android.os.Parcelable { method public int describeContents(); + method public android.view.accessibility.AccessibilityNodeInfo getAnchor(); method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityWindowInfo getChild(int); method public int getChildCount(); @@ -47010,6 +47011,7 @@ package android.view.accessibility { method public int getLayer(); method public android.view.accessibility.AccessibilityWindowInfo getParent(); method public android.view.accessibility.AccessibilityNodeInfo getRoot(); + method public java.lang.CharSequence getTitle(); method public int getType(); method public boolean isAccessibilityFocused(); method public boolean isActive(); diff --git a/api/test-current.txt b/api/test-current.txt index eeb303028073..f60aab14c172 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44334,6 +44334,7 @@ package android.view.accessibility { public final class AccessibilityWindowInfo implements android.os.Parcelable { method public int describeContents(); + method public android.view.accessibility.AccessibilityNodeInfo getAnchor(); method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityWindowInfo getChild(int); method public int getChildCount(); @@ -44341,6 +44342,7 @@ package android.view.accessibility { method public int getLayer(); method public android.view.accessibility.AccessibilityWindowInfo getParent(); method public android.view.accessibility.AccessibilityNodeInfo getRoot(); + method public java.lang.CharSequence getTitle(); method public int getType(); method public boolean isAccessibilityFocused(); method public boolean isActive(); diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index 7a0c89b75a4a..e18a34de3f91 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -36,8 +36,7 @@ import java.util.List; * Accessibility services with the * {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch * gestures. This class describes those gestures. Gestures are made up of one or more strokes. - * Gestures are immutable; use the {@code create} methods to get common gesture, or a - * {@code Builder} to create a new one. + * Gestures are immutable once built. * <p> * Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds. */ diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 3a17e23b0547..ed5dfa5393ec 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -883,6 +883,15 @@ public class IntentFilter implements Parcelable { return true; } + @Override + public boolean equals(Object obj) { + if (obj instanceof AuthorityEntry) { + final AuthorityEntry other = (AuthorityEntry)obj; + return match(other); + } + return false; + } + /** * Determine whether this AuthorityEntry matches the given data Uri. * <em>Note that this comparison is case-sensitive, unlike formal @@ -917,7 +926,7 @@ public class IntentFilter implements Parcelable { } return MATCH_CATEGORY_HOST; } - }; + } /** * Add a new Intent data "scheme specific part" to match against. The filter must diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6b79a8ac438a..9d53a0001a84 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -295,9 +295,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean mLastShowInputRequested; int mCandidatesVisibility; CompletionInfo[] mCurCompletions; - - boolean mShowInputForced; - + boolean mFullscreenApplied; boolean mIsFullscreen; View mExtractView; @@ -422,7 +420,6 @@ public class InputMethodService extends AbstractInputMethodService { boolean wasVis = isInputViewShown(); mShowInputFlags = 0; mShowInputRequested = false; - mShowInputForced = false; doHideWindow(); clearInsetOfPreviousIme(); if (resultReceiver != null) { @@ -439,8 +436,7 @@ public class InputMethodService extends AbstractInputMethodService { public void showSoftInput(int flags, ResultReceiver resultReceiver) { if (DEBUG) Log.v(TAG, "showSoftInput()"); boolean wasVis = isInputViewShown(); - mShowInputFlags = 0; - if (onShowInputRequested(flags, false)) { + if (dispatchOnShowInputRequested(flags, false)) { try { showWindow(true); } catch (BadTokenException e) { @@ -817,8 +813,8 @@ public class InputMethodService extends AbstractInputMethodService { mInitialized = false; mWindowCreated = false; mShowInputRequested = false; - mShowInputForced = false; - + mShowInputFlags = 0; + mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService); mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); @@ -888,7 +884,7 @@ public class InputMethodService extends AbstractInputMethodService { */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - + boolean visible = mWindowVisible; int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; @@ -903,7 +899,7 @@ public class InputMethodService extends AbstractInputMethodService { if (visible) { if (showingInput) { // If we were last showing the soft keyboard, try to do so again. - if (onShowInputRequested(showFlags, true)) { + if (dispatchOnShowInputRequested(showFlags, true)) { showWindow(true); if (completions != null) { mCurCompletions = completions; @@ -1540,20 +1536,41 @@ public class InputMethodService extends AbstractInputMethodService { return false; } } - if ((flags&InputMethod.SHOW_FORCED) != 0) { - mShowInputForced = true; - } return true; } - + + /** + * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal + * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is + * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have + * to have this method to ensure that those internal states are always updated no matter how + * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author. + * @param flags Provides additional information about the show request, + * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}. + * @param configChange This is true if we are re-showing due to a + * configuration change. + * @return Returns true to indicate that the window should be shown. + * @see #onShowInputRequested(int, boolean) + */ + private boolean dispatchOnShowInputRequested(int flags, boolean configChange) { + final boolean result = onShowInputRequested(flags, configChange); + if (result) { + mShowInputFlags = flags; + } else { + mShowInputFlags = 0; + } + return result; + } + public void showWindow(boolean showInput) { if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput + " mShowInputRequested=" + mShowInputRequested + " mWindowAdded=" + mWindowAdded + " mWindowCreated=" + mWindowCreated + " mWindowVisible=" + mWindowVisible - + " mInputStarted=" + mInputStarted); - + + " mInputStarted=" + mInputStarted + + " mShowInputFlags=" + mShowInputFlags); + if (mInShowWindow) { Log.w(TAG, "Re-entrance in to showWindow"); return; @@ -2573,7 +2590,6 @@ public class InputMethodService extends AbstractInputMethodService { p.println(" mShowInputRequested=" + mShowInputRequested + " mLastShowInputRequested=" + mLastShowInputRequested - + " mShowInputForced=" + mShowInputForced + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags)); p.println(" mCandidatesVisibility=" + mCandidatesVisibility + " mFullscreenApplied=" + mFullscreenApplied diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index b72107447541..737e46071313 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -44,6 +44,8 @@ public class WindowInfo implements Parcelable { public boolean focused; public final Rect boundsInScreen = new Rect(); public List<IBinder> childTokens; + public CharSequence title; + public int accessibilityIdOfAnchor = View.NO_ID; private WindowInfo() { /* do nothing - hide constructor */ @@ -65,6 +67,8 @@ public class WindowInfo implements Parcelable { window.parentToken = other.parentToken; window.focused = other.focused; window.boundsInScreen.set(other.boundsInScreen); + window.title = other.title; + window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; if (other.childTokens != null && !other.childTokens.isEmpty()) { if (window.childTokens == null) { @@ -95,6 +99,8 @@ public class WindowInfo implements Parcelable { parcel.writeStrongBinder(parentToken); parcel.writeInt(focused ? 1 : 0); boundsInScreen.writeToParcel(parcel, flags); + parcel.writeCharSequence(title); + parcel.writeInt(accessibilityIdOfAnchor); if (childTokens != null && !childTokens.isEmpty()) { parcel.writeInt(1); @@ -108,13 +114,15 @@ public class WindowInfo implements Parcelable { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("WindowInfo["); - builder.append("type=").append(type); + builder.append("title=").append(title); + builder.append(", type=").append(type); builder.append(", layer=").append(layer); builder.append(", token=").append(token); builder.append(", bounds=").append(boundsInScreen); builder.append(", parent=").append(parentToken); builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); + builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor); builder.append(']'); return builder.toString(); } @@ -126,6 +134,8 @@ public class WindowInfo implements Parcelable { parentToken = parcel.readStrongBinder(); focused = (parcel.readInt() == 1); boundsInScreen.readFromParcel(parcel); + title = parcel.readCharSequence(); + accessibilityIdOfAnchor = parcel.readInt(); final boolean hasChildren = (parcel.readInt() == 1); if (hasChildren) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 03dcf99258ab..c372c30915f2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1693,6 +1693,14 @@ public interface WindowManager extends ViewManager { */ public long userActivityTimeout = -1; + /** + * For windows with an anchor (e.g. PopupWindow), keeps track of the View that anchors the + * window. + * + * @hide + */ + public int accessibilityIdOfAnchor = -1; + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -1799,6 +1807,7 @@ public interface WindowManager extends ViewManager { out.writeInt(surfaceInsets.bottom); out.writeInt(hasManualSurfaceInsets ? 1 : 0); out.writeInt(needsMenuKey); + out.writeInt(accessibilityIdOfAnchor); } public static final Parcelable.Creator<LayoutParams> CREATOR @@ -1849,6 +1858,7 @@ public interface WindowManager extends ViewManager { surfaceInsets.bottom = in.readInt(); hasManualSurfaceInsets = in.readInt() != 0; needsMenuKey = in.readInt(); + accessibilityIdOfAnchor = in.readInt(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -1888,6 +1898,8 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23; /** {@hide} */ + public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24; + /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; // internal buffer to backup/restore parameters under compatibility mode. @@ -2048,6 +2060,11 @@ public interface WindowManager extends ViewManager { changes |= NEEDS_MENU_KEY_CHANGED; } + if (accessibilityIdOfAnchor != o.accessibilityIdOfAnchor) { + accessibilityIdOfAnchor = o.accessibilityIdOfAnchor; + changes |= ACCESSIBILITY_ANCHOR_CHANGED; + } + return changes; } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 2cde03d63529..f8a13a373aec 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -240,10 +240,12 @@ import java.util.List; * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text * was traversed.</li> * <li>{@link #getText()} - The text of the source's sub-tree.</li> - * <li>{@link #getFromIndex()} - The start of the next/previous text at the specified granularity - * - inclusive.</li> - * <li>{@link #getToIndex()} - The end of the next/previous text at the specified granularity - * - exclusive.</li> + * <li>{@link #getFromIndex()} - The start the text that was skipped over in this movement. + * This is the starting point when moving forward through the text, but not when moving + * back.</li> + * <li>{@link #getToIndex()} - The end of the text that was skipped over in this movement. + * This is the ending point when moving forward through the text, but not when moving + * back.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getContentDescription()} - The content description of the source.</li> diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index ad78b686b8de..d0d4507e7b62 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -89,6 +89,8 @@ public final class AccessibilityWindowInfo implements Parcelable { private int mParentId = UNDEFINED; private final Rect mBoundsInScreen = new Rect(); private LongArray mChildIds; + private CharSequence mTitle; + private int mAnchorId = UNDEFINED; private int mConnectionId = UNDEFINED; @@ -97,6 +99,26 @@ public final class AccessibilityWindowInfo implements Parcelable { } /** + * Gets the title of the window. + * + * @return The title. + */ + public CharSequence getTitle() { + return mTitle; + } + + /** + * Sets the title of the window. + * + * @param title The title. + * + * @hide + */ + public void setTitle(CharSequence title) { + mTitle = title; + } + + /** * Gets the type of the window. * * @return The type. @@ -159,9 +181,35 @@ public final class AccessibilityWindowInfo implements Parcelable { } /** - * Gets the parent window if such. + * Sets the anchor node's ID. + * + * @param anchorId The anchor's accessibility id in its window. + * + * @hide + */ + public void setAnchorId(int anchorId) { + mAnchorId = anchorId; + } + + /** + * Gets the node that anchors this window to another. + * + * @return The anchor node, or {@code null} if none exists. + */ + public AccessibilityNodeInfo getAnchor() { + if ((mConnectionId == UNDEFINED) || (mAnchorId == UNDEFINED) || (mParentId == UNDEFINED)) { + return null; + } + + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + mParentId, mAnchorId, true, 0); + } + + /** + * Gets the parent window. * - * @return The parent window. + * @return The parent window, or {@code null} if none exists. */ public AccessibilityWindowInfo getParent() { if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) { @@ -370,6 +418,8 @@ public final class AccessibilityWindowInfo implements Parcelable { infoClone.mId = info.mId; infoClone.mParentId = info.mParentId; infoClone.mBoundsInScreen.set(info.mBoundsInScreen); + infoClone.mTitle = info.mTitle; + infoClone.mAnchorId = info.mAnchorId; if (info.mChildIds != null && info.mChildIds.size() > 0) { if (infoClone.mChildIds == null) { @@ -410,6 +460,8 @@ public final class AccessibilityWindowInfo implements Parcelable { parcel.writeInt(mId); parcel.writeInt(mParentId); mBoundsInScreen.writeToParcel(parcel, flags); + parcel.writeCharSequence(mTitle); + parcel.writeInt(mAnchorId); final LongArray childIds = mChildIds; if (childIds == null) { @@ -432,6 +484,8 @@ public final class AccessibilityWindowInfo implements Parcelable { mId = parcel.readInt(); mParentId = parcel.readInt(); mBoundsInScreen.readFromParcel(parcel); + mTitle = parcel.readCharSequence(); + mAnchorId = parcel.readInt(); final int childCount = parcel.readInt(); if (childCount > 0) { @@ -471,6 +525,7 @@ public final class AccessibilityWindowInfo implements Parcelable { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("AccessibilityWindowInfo["); + builder.append("title=").append(mTitle); builder.append("id=").append(mId); builder.append(", type=").append(typeToString(mType)); builder.append(", layer=").append(mLayer); @@ -494,6 +549,7 @@ public final class AccessibilityWindowInfo implements Parcelable { builder.append(']'); } else { builder.append(", hasParent=").append(mParentId != UNDEFINED); + builder.append(", isAnchored=").append(mAnchorId != UNDEFINED); builder.append(", hasChildren=").append(mChildIds != null && mChildIds.size() > 0); } @@ -515,6 +571,8 @@ public final class AccessibilityWindowInfo implements Parcelable { mChildIds.clear(); } mConnectionId = UNDEFINED; + mAnchorId = UNDEFINED; + mTitle = null; } /** diff --git a/core/java/android/widget/AccessibilityIterators.java b/core/java/android/widget/AccessibilityIterators.java index a3d58a4e2060..442ffa136ca3 100644 --- a/core/java/android/widget/AccessibilityIterators.java +++ b/core/java/android/widget/AccessibilityIterators.java @@ -134,8 +134,8 @@ final class AccessibilityIterators { @Override public int[] following(int offset) { - final int textLegth = mText.length(); - if (textLegth <= 0) { + final int textLength = mText.length(); + if (textLength <= 0) { return null; } if (offset >= mText.length()) { @@ -163,8 +163,8 @@ final class AccessibilityIterators { @Override public int[] preceding(int offset) { - final int textLegth = mText.length(); - if (textLegth <= 0) { + final int textLength = mText.length(); + if (textLength <= 0) { return null; } if (offset <= 0) { @@ -181,8 +181,13 @@ final class AccessibilityIterators { final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop() - mView.getTotalPaddingBottom(); final int previousPageEndY = currentLineTop - pageHeight; - final int currentPageStartLine = (previousPageEndY > 0) ? - mLayout.getLineForVertical(previousPageEndY) + 1 : 0; + int currentPageStartLine = (previousPageEndY > 0) ? + mLayout.getLineForVertical(previousPageEndY) : 0; + // If we're at the end of text, we're at the end of the current line rather than the + // start of the next line, so we should move up one fewer lines than we would otherwise. + if (end == mText.length() && (currentPageStartLine < currentLine)) { + currentPageStartLine += 1; + } final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START); diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 434e3eb8237e..ad355504a0ee 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -155,6 +155,9 @@ public class EditText extends TextView { public boolean performAccessibilityActionInternal(int action, Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_SET_TEXT: { + if (!isEnabled()) { + return false; + } CharSequence text = (arguments != null) ? arguments.getCharSequence( AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE) : null; setText(text); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 92631da2da90..c471cf3d74a1 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1214,6 +1214,7 @@ public class PopupWindow { final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, p.width, p.height, gravity); updateAboveAnchor(aboveAnchor); + p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1; invokePopup(p); } @@ -1984,6 +1985,13 @@ public class PopupWindow { update = true; } + int newAccessibilityIdOfAnchor = + (mAnchor != null) ? mAnchor.get().getAccessibilityViewId() : -1; + if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) { + p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor; + update = true; + } + if (update) { setLayoutDirectionFromAnchor(); mWindowManager.updateViewLayout(mDecorView, p); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 15d1bd6e08c6..da0768e4b604 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9094,6 +9094,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mBufferType == BufferType.EDITABLE) { info.setEditable(true); + if (isEnabled()) { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT); + } } if (mEditor != null) { @@ -9225,6 +9228,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } return false; + case AccessibilityNodeInfo.ACTION_SET_TEXT: { + if (!isEnabled() || (mBufferType != BufferType.EDITABLE)) { + return false; + } + CharSequence text = (arguments != null) ? arguments.getCharSequence( + AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE) : null; + setText(text); + if (text != null && text.length() > 0) { + Selection.setSelection((Spannable) mText, text.length()); + } + } return true; default: { return super.performAccessibilityActionInternal(action, arguments); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index d80b63acb90f..0f257d78d5f5 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -24,6 +24,7 @@ import android.app.ActivityManagerNative; import android.app.SearchManager; import android.os.UserHandle; +import android.text.TextUtils; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.IRotationWatcher.Stub; @@ -514,6 +515,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mDecorContentParent.setWindowTitle(title); } mTitle = title; + WindowManager.LayoutParams params = getAttributes(); + if (!TextUtils.equals(title, params.getTitle())) { + params.setTitle(title); + dispatchWindowAttributesChanged(getAttributes()); + } } @Override diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java index 177344a0ed4c..24a400e48189 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -275,16 +275,23 @@ final public class MediaExtractor { return initDataMap.get(schemeUuid); } }; - } else if (formatMap.containsKey("crypto-key")) { - ByteBuffer buf = (ByteBuffer) formatMap.get("crypto-key"); - buf.rewind(); - final byte[] data = new byte[buf.remaining()]; - buf.get(data); - return new DrmInitData() { - public SchemeInitData get(UUID schemeUuid) { - return new DrmInitData.SchemeInitData("webm", data); + } else { + int numTracks = getTrackCount(); + for (int i = 0; i < numTracks; ++i) { + Map<String, Object> trackFormatMap = getTrackFormatNative(i); + if (!trackFormatMap.containsKey("crypto-key")) { + continue; } - }; + ByteBuffer buf = (ByteBuffer) trackFormatMap.get("crypto-key"); + buf.rewind(); + final byte[] data = new byte[buf.remaining()]; + buf.get(data); + return new DrmInitData() { + public SchemeInitData get(UUID schemeUuid) { + return new DrmInitData.SchemeInitData("webm", data); + } + }; + } } return null; } diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk new file mode 100644 index 000000000000..537b1714ba71 --- /dev/null +++ b/packages/CtsShim/Android.mk @@ -0,0 +1,61 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +########################################################### +# Variant: Privileged app + +include $(CLEAR_VARS) +# this needs to be a privileged application +LOCAL_PRIVILEGED_MODULE := true + +LOCAL_MODULE_TAGS := optional +LOCAL_SDK_VERSION := current +LOCAL_PROGUARD_ENABLED := disabled +LOCAL_DEX_PREOPT := false + +LOCAL_PACKAGE_NAME := CtsShimPriv + +#TODO need to find the correct certificate +#Change in conjunction with cts/hostsidetests/appsecurity/test-apps/IntentFilterApp +LOCAL_CERTIFICATE := platform +LOCAL_MANIFEST_FILE := priv_shim/AndroidManifest.xml + +include $(BUILD_PACKAGE) + + + +########################################################### +# Variant: System app + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional +LOCAL_SDK_VERSION := current +LOCAL_PROGUARD_ENABLED := disabled +LOCAL_DEX_PREOPT := false + +LOCAL_PACKAGE_NAME := CtsShim + +#TODO need to find the correct certificate +#Change in conjunction with cts/hostsidetests/appsecurity/test-apps/IntentFilterApp +LOCAL_CERTIFICATE := platform +LOCAL_MANIFEST_FILE := shim/AndroidManifest.xml + +include $(BUILD_PACKAGE) + + diff --git a/packages/CtsShim/priv_shim/AndroidManifest.xml b/packages/CtsShim/priv_shim/AndroidManifest.xml new file mode 100644 index 000000000000..0a3f8235948c --- /dev/null +++ b/packages/CtsShim/priv_shim/AndroidManifest.xml @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Manifest for the privileged CTS shim --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.cts.priv.ctsshim"> + <application android:label="CtsShim"> + + <!-- These activities don't actually exist; define them just to test the filters !--> + + <!-- install test; [some] high priority filters granted --> + <activity android:name=".InstallPriority"> + <!-- normal actions; priority will be granted --> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEARCH" /> + <category android:name="android.intent.category.INFO" /> + </intent-filter> + + <!-- protected actions; priority will be denied --> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEND" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEND_MULTIPLE" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SENDTO" /> + </intent-filter> + </activity> + + <!-- upgrade test; single, equivalent filter --> + <activity android:name=".UpgradeMatch"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.MATCH" /> + <category android:name="android.intent.category.INFO" /> + </intent-filter> + </activity> + + <!-- upgrade test; multiple, equivalent filters --> + <activity android:name=".UpgradeMatchMultiple"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.MATCH_MULTIPLE" /> + <category android:name="android.intent.category.INFO" /> + </intent-filter> + + <intent-filter android:priority="150"> + <action android:name="com.android.cts.action.MATCH_MULTIPLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http" /> + <data android:scheme="https" /> + <data android:host="www.google.com" android:port="80" /> + <data android:host="www.google.com" android:port="8080" /> + <data android:host="goo.gl" android:port="443" /> + </intent-filter> + </activity> + + <!-- upgrade test; lower priority --> + <activity android:name=".UpgradeLowerPriority"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.LOWER_PRIORITY" /> + <category android:name="android.intent.category.INFO" /> + </intent-filter> + </activity> + + <!-- upgrade test; action subset --> + <activity android:name=".UpgradeActionSubset"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.ACTION_SUB" /> + <action android:name="com.android.cts.action.ACTION_SUB_2" /> + <action android:name="com.android.cts.action.ACTION_SUB_3" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <!-- upgrade test; category subset --> + <activity android:name=".UpgradeCategorySubset"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.CATEGORY_SUB" /> + <category android:name="android.intent.category.INFO" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <!-- upgrade test; scheme subset --> + <activity android:name=".UpgradeSchemeSubset"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.SCHEME_SUB" /> + <data android:scheme="content" /> + <data android:scheme="flubber" /> + <data android:scheme="zoodle" /> + </intent-filter> + </activity> + + <!-- upgrade test; authority subset --> + <activity android:name=".UpgradeAuthoritySubset"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.AUTHORITY_SUB" /> + <data android:host="www.google.com" android:port="80" /> + <data android:host="www.google.com" android:port="8080" /> + <data android:host="mail.google.com" android:port="80" /> + <data android:host="goo.gl" android:port="443" /> + </intent-filter> + </activity> + + + <!-- upgrade test; new action --> + <activity android:name=".UpgradeNewAction"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.NEW_ACTION" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <!-- upgrade test; new category --> + <activity android:name=".UpgradeNewCategory"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.NEW_CATEGORY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <!-- upgrade test; new scheme --> + <activity android:name=".UpgradeNewScheme"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.NEW_SCHEME" /> + <data android:scheme="content" /> + </intent-filter> + </activity> + + <!-- upgrade test; new authority --> + <activity android:name=".UpgradeNewAuthority"> + <intent-filter android:priority="100"> + <action android:name="com.android.cts.action.NEW_AUTHORITY" /> + <data android:host="www.google.com" android:port="80" /> + </intent-filter> + </activity> + + </application> +</manifest> + diff --git a/packages/CtsShim/shim/AndroidManifest.xml b/packages/CtsShim/shim/AndroidManifest.xml new file mode 100644 index 000000000000..ee4b547ddc97 --- /dev/null +++ b/packages/CtsShim/shim/AndroidManifest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Manifest for the system CTS shim --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.cts.system.ctsshim"> + <application android:label="CtsShim"> + + <!-- These activities don't actually exist; define them just to test the filters !--> + + <!-- install test; high priority filter DENIED --> + <activity android:name=".InstallPriority"> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEARCH" /> + <category android:name="android.intent.category.INFO" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.BROWSABLE" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEND" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SEND_MULTIPLE" /> + </intent-filter> + <intent-filter android:priority="100"> + <action android:name="android.intent.action.SENDTO" /> + </intent-filter> + </activity> + + </application> +</manifest> + diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index a5798e2d99ae..e256ecd6b85e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1784,10 +1784,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode); if (!serviceChangingSoftKeyboardModeIsEnabled) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - 0, - userState.mUserId); + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + 0, + userState.mUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } userState.mSoftKeyboardShowMode = 0; userState.mServiceChangingSoftKeyboardMode = null; } @@ -3491,6 +3496,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { reportedWindow.setLayer(window.layer); reportedWindow.setFocused(window.focused); reportedWindow.setBoundsInScreen(window.boundsInScreen); + reportedWindow.setTitle(window.title); + reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); final int parentId = findWindowIdLocked(window.parentToken); if (parentId >= 0) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index b8cbf16e9f6f..811e34e00bd2 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -125,6 +125,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.RadioButton; import android.widget.Switch; import android.widget.TextView; +import android.widget.Toast; import java.io.File; import java.io.FileDescriptor; @@ -452,6 +453,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private AlertDialog.Builder mDialogBuilder; private AlertDialog mSwitchingDialog; private View mSwitchingDialogTitleView; + private Toast mSubtypeSwitchedByShortCutToast; private InputMethodInfo[] mIms; private int[] mSubtypeIds; private LocaleList mLastSystemLocales; @@ -2952,6 +2954,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId); + if (mSubtypeSwitchedByShortCutToast != null) { + mSubtypeSwitchedByShortCutToast.cancel(); + mSubtypeSwitchedByShortCutToast = null; + } + if ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0) { + // IME window is shown. The user should be able to visually understand that the + // subtype is changed in most of cases. To avoid UI overlap, we do not show a toast + // in this case. + return; + } + final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId); + if (newInputMethodInfo == null) { + return; + } + final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext, + newInputMethodInfo, mCurrentSubtype); + if (!TextUtils.isEmpty(toastText)) { + mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText.toString(), + Toast.LENGTH_SHORT); + mSubtypeSwitchedByShortCutToast.show(); + } } } @@ -3823,6 +3846,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private static String imeWindowStatusToString(final int imeWindowVis) { + final StringBuilder sb = new StringBuilder(); + boolean first = true; + if ((imeWindowVis & InputMethodService.IME_ACTIVE) != 0) { + sb.append("Active"); + first = false; + } + if ((imeWindowVis & InputMethodService.IME_VISIBLE) != 0) { + if (!first) { + sb.append("|"); + } + sb.append("Visible"); + } + return sb.toString(); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -3870,6 +3909,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub method = mCurMethod; p.println(" mCurMethod=" + mCurMethod); p.println(" mEnabledSession=" + mEnabledSession); + p.println(" mImeWindowVis=" + imeWindowStatusToString(mImeWindowVis)); p.println(" mShowRequested=" + mShowRequested + " mShowExplicitlyRequested=" + mShowExplicitlyRequested + " mShowForced=" + mShowForced diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index af9f002756af..4ce730ff90a0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -76,6 +76,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED; import static android.content.pm.PackageParser.isApkFile; +import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; @@ -115,6 +116,7 @@ import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentFilter.AuthorityEntry; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; @@ -147,6 +149,7 @@ import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ActivityIntentInfo; +import android.content.pm.PackageParser.IntentInfo; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageStats; @@ -320,6 +323,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_INTENT_MATCHING = false; private static final boolean DEBUG_PACKAGE_SCANNING = false; private static final boolean DEBUG_VERIFY = false; + private static final boolean DEBUG_FILTERS = false; // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService // and PackageDexOptimizer. All these classes have their own flag to allow switching a single @@ -445,6 +449,18 @@ public class PackageManagerService extends IPackageManager.Stub { sBrowserIntent.setData(Uri.parse("http:")); } + /** + * The set of all protected actions [i.e. those actions for which a high priority + * intent filter is disallowed]. + */ + private static final Set<String> PROTECTED_ACTIONS = new ArraySet<>(); + static { + PROTECTED_ACTIONS.add(Intent.ACTION_SEND); + PROTECTED_ACTIONS.add(Intent.ACTION_SENDTO); + PROTECTED_ACTIONS.add(Intent.ACTION_SEND_MULTIPLE); + PROTECTED_ACTIONS.add(Intent.ACTION_VIEW); + } + // Compilation reasons. public static final int REASON_FIRST_BOOT = 0; public static final int REASON_BOOT = 1; @@ -531,6 +547,20 @@ public class PackageManagerService extends IPackageManager.Stub { * are package location. */ final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); + /** + * Tracks high priority intent filters for protected actions. During boot, certain + * filter actions are protected and should never be allowed to have a high priority + * intent filter for them. However, there is one, and only one exception -- the + * setup wizard. It must be able to define a high priority intent filter for these + * actions to ensure there are no escapes from the wizard. We need to delay processing + * of these during boot as we need to look at all of the system packages in order + * to know which component is the setup wizard. + */ + private final List<PackageParser.ActivityIntentInfo> mProtectedFilters = new ArrayList<>(); + /** + * Whether or not processing protected filters should be deferred. + */ + private boolean mDeferProtectedFilters = true; /** * Tracks existing system packages prior to receiving an OTA. Keys are package name. @@ -2429,6 +2459,36 @@ public class PackageManagerService extends IPackageManager.Stub { } mExpectingBetter.clear(); + // Resolve protected action filters. Only the setup wizard is allowed to + // have a high priority filter for these actions. + mSetupWizardPackage = getSetupWizardPackageName(); + if (mProtectedFilters.size() > 0) { + if (DEBUG_FILTERS && mSetupWizardPackage == null) { + Slog.i(TAG, "No setup wizard;" + + " All protected intents capped to priority 0"); + } + for (ActivityIntentInfo filter : mProtectedFilters) { + if (filter.activity.info.packageName.equals(mSetupWizardPackage)) { + if (DEBUG_FILTERS) { + Slog.i(TAG, "Found setup wizard;" + + " allow priority " + filter.getPriority() + ";" + + " package: " + filter.activity.info.packageName + + " activity: " + filter.activity.className + + " priority: " + filter.getPriority()); + } + // skip setup wizard; allow it to keep the high priority filter + continue; + } + Slog.w(TAG, "Protected action; cap priority to 0;" + + " package: " + filter.activity.info.packageName + + " activity: " + filter.activity.className + + " origPrio: " + filter.getPriority()); + filter.setPriority(0); + } + } + mDeferProtectedFilters = false; + mProtectedFilters.clear(); + // Now that we know all of the shared libraries, update all clients to have // the correct library paths. updateAllSharedLibrariesLPw(); @@ -2531,7 +2591,6 @@ public class PackageManagerService extends IPackageManager.Stub { } mInstallerService = new PackageInstallerService(context, this); - mSetupWizardPackage = getSetupWizardPackageName(); final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr(); final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr(); @@ -9762,8 +9821,314 @@ public class PackageManagerService extends IPackageManager.Stub { return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId); } + /** + * Finds a privileged activity that matches the specified activity names. + */ + private PackageParser.Activity findMatchingActivity( + List<PackageParser.Activity> activityList, ActivityInfo activityInfo) { + for (PackageParser.Activity sysActivity : activityList) { + if (sysActivity.info.name.equals(activityInfo.name)) { + return sysActivity; + } + if (sysActivity.info.name.equals(activityInfo.targetActivity)) { + return sysActivity; + } + if (sysActivity.info.targetActivity != null) { + if (sysActivity.info.targetActivity.equals(activityInfo.name)) { + return sysActivity; + } + if (sysActivity.info.targetActivity.equals(activityInfo.targetActivity)) { + return sysActivity; + } + } + } + return null; + } + + public class IterGenerator<E> { + public Iterator<E> generate(ActivityIntentInfo info) { + return null; + } + } + + public class ActionIterGenerator extends IterGenerator<String> { + @Override + public Iterator<String> generate(ActivityIntentInfo info) { + return info.actionsIterator(); + } + } + + public class CategoriesIterGenerator extends IterGenerator<String> { + @Override + public Iterator<String> generate(ActivityIntentInfo info) { + return info.categoriesIterator(); + } + } + + public class SchemesIterGenerator extends IterGenerator<String> { + @Override + public Iterator<String> generate(ActivityIntentInfo info) { + return info.schemesIterator(); + } + } + + public class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> { + @Override + public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) { + return info.authoritiesIterator(); + } + } + + /** + * <em>WARNING</em> for performance reasons, the passed in intentList WILL BE + * MODIFIED. Do not pass in a list that should not be changed. + */ + private <T> void getIntentListSubset(List<ActivityIntentInfo> intentList, + IterGenerator<T> generator, Iterator<T> searchIterator) { + // loop through the set of actions; every one must be found in the intent filter + while (searchIterator.hasNext()) { + // we must have at least one filter in the list to consider a match + if (intentList.size() == 0) { + break; + } + + final T searchAction = searchIterator.next(); + + // loop through the set of intent filters + final Iterator<ActivityIntentInfo> intentIter = intentList.iterator(); + while (intentIter.hasNext()) { + final ActivityIntentInfo intentInfo = intentIter.next(); + boolean selectionFound = false; + + // loop through the intent filter's selection criteria; at least one + // of them must match the searched criteria + final Iterator<T> intentSelectionIter = generator.generate(intentInfo); + while (intentSelectionIter != null && intentSelectionIter.hasNext()) { + final T intentSelection = intentSelectionIter.next(); + if (intentSelection != null && intentSelection.equals(searchAction)) { + selectionFound = true; + break; + } + } + + // the selection criteria wasn't found in this filter's set; this filter + // is not a potential match + if (!selectionFound) { + intentIter.remove(); + } + } + } + } + + private boolean isProtectedAction(ActivityIntentInfo filter) { + final Iterator<String> actionsIter = filter.actionsIterator(); + while (actionsIter != null && actionsIter.hasNext()) { + final String filterAction = actionsIter.next(); + if (PROTECTED_ACTIONS.contains(filterAction)) { + return true; + } + } + return false; + } + + /** + * Adjusts the priority of the given intent filter according to policy. + * <p> + * <ul> + * <li>The priority for non privileged applications is capped to '0'</li> + * <li>The priority for protected actions on privileged applications is capped to '0'</li> + * <li>The priority for unbundled updates to privileged applications is capped to the + * priority defined on the system partition</li> + * </ul> + * <p> + * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is + * allowed to obtain any priority on any action. + */ + private void adjustPriority( + List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) { + // nothing to do; priority is fine as-is + if (intent.getPriority() <= 0) { + return; + } + + final ActivityInfo activityInfo = intent.activity.info; + final ApplicationInfo applicationInfo = activityInfo.applicationInfo; + + final boolean privilegedApp = + ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0); + if (!privilegedApp) { + // non-privileged applications can never define a priority >0 + Slog.w(TAG, "Non-privileged app; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + intent.setPriority(0); + return; + } + + if (systemActivities == null) { + // the system package is not disabled; we're parsing the system partition + if (isProtectedAction(intent)) { + if (mDeferProtectedFilters) { + // We can't deal with these just yet. No component should ever obtain a + // >0 priority for a protected actions, with ONE exception -- the setup + // wizard. The setup wizard, however, cannot be known until we're able to + // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do + // until all intent filters have been processed. Chicken, meet egg. + // Let the filter temporarily have a high priority and rectify the + // priorities after all system packages have been scanned. + mProtectedFilters.add(intent); + if (DEBUG_FILTERS) { + Slog.i(TAG, "Protected action; save for later;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + return; + } else { + if (DEBUG_FILTERS && mSetupWizardPackage == null) { + Slog.i(TAG, "No setup wizard;" + + " All protected intents capped to priority 0"); + } + if (intent.activity.info.packageName.equals(mSetupWizardPackage)) { + if (DEBUG_FILTERS) { + Slog.i(TAG, "Found setup wizard;" + + " allow priority " + intent.getPriority() + ";" + + " package: " + intent.activity.info.packageName + + " activity: " + intent.activity.className + + " priority: " + intent.getPriority()); + } + // setup wizard gets whatever it wants + return; + } + Slog.w(TAG, "Protected action; cap priority to 0;" + + " package: " + intent.activity.info.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + intent.setPriority(0); + return; + } + } + // privileged apps on the system image get whatever priority they request + return; + } + + // privileged app unbundled update ... try to find the same activity + final PackageParser.Activity foundActivity = + findMatchingActivity(systemActivities, activityInfo); + if (foundActivity == null) { + // this is a new activity; it cannot obtain >0 priority + if (DEBUG_FILTERS) { + Slog.i(TAG, "New activity; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); + return; + } + + // found activity, now check for filter equivalence + + // a shallow copy is enough; we modify the list, not its contents + final List<ActivityIntentInfo> intentListCopy = + new ArrayList<>(foundActivity.intents); + final List<ActivityIntentInfo> foundFilters = findFilters(intent); + + // find matching action subsets + final Iterator<String> actionsIterator = intent.actionsIterator(); + if (actionsIterator != null) { + getIntentListSubset( + intentListCopy, new ActionIterGenerator(), actionsIterator); + if (intentListCopy.size() == 0) { + // no more intents to match; we're not equivalent + if (DEBUG_FILTERS) { + Slog.i(TAG, "Mismatched action; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); + return; + } + } + + // find matching category subsets + final Iterator<String> categoriesIterator = intent.categoriesIterator(); + if (categoriesIterator != null) { + getIntentListSubset(intentListCopy, new CategoriesIterGenerator(), + categoriesIterator); + if (intentListCopy.size() == 0) { + // no more intents to match; we're not equivalent + if (DEBUG_FILTERS) { + Slog.i(TAG, "Mismatched category; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); + return; + } + } + + // find matching schemes subsets + final Iterator<String> schemesIterator = intent.schemesIterator(); + if (schemesIterator != null) { + getIntentListSubset(intentListCopy, new SchemesIterGenerator(), + schemesIterator); + if (intentListCopy.size() == 0) { + // no more intents to match; we're not equivalent + if (DEBUG_FILTERS) { + Slog.i(TAG, "Mismatched scheme; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); + return; + } + } + + // find matching authorities subsets + final Iterator<IntentFilter.AuthorityEntry> + authoritiesIterator = intent.authoritiesIterator(); + if (authoritiesIterator != null) { + getIntentListSubset(intentListCopy, + new AuthoritiesIterGenerator(), + authoritiesIterator); + if (intentListCopy.size() == 0) { + // no more intents to match; we're not equivalent + if (DEBUG_FILTERS) { + Slog.i(TAG, "Mismatched authority; cap priority to 0;" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); + return; + } + } + + // we found matching filter(s); app gets the max priority of all intents + int cappedPriority = 0; + for (int i = intentListCopy.size() - 1; i >= 0; --i) { + cappedPriority = Math.max(cappedPriority, intentListCopy.get(i).getPriority()); + } + if (intent.getPriority() > cappedPriority) { + if (DEBUG_FILTERS) { + Slog.i(TAG, "Found matching filter(s);" + + " cap priority to " + cappedPriority + ";" + + " package: " + applicationInfo.packageName + + " activity: " + intent.activity.className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(cappedPriority); + return; + } + // all this for nothing; the requested priority was <= what was on the system + } + public final void addActivity(PackageParser.Activity a, String type) { - final boolean systemApp = a.info.applicationInfo.isSystemApp(); mActivities.put(a.getComponentName(), a); if (DEBUG_SHOW_INFO) Log.v( @@ -9774,10 +10139,12 @@ public class PackageManagerService extends IPackageManager.Stub { final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); - if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { - intent.setPriority(0); - Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " - + a.className + " with priority > 0, forcing to 0"); + if ("activity".equals(type)) { + final PackageSetting ps = + mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName); + final List<PackageParser.Activity> systemActivities = + ps != null && ps.pkg != null ? ps.pkg.activities : null; + adjustPriority(systemActivities, intent); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); @@ -9927,18 +10294,6 @@ public class PackageManagerService extends IPackageManager.Stub { out.println(); } -// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) { -// final Iterator<ResolveInfo> i = resolveInfoList.iterator(); -// final List<ResolveInfo> retList = Lists.newArrayList(); -// while (i.hasNext()) { -// final ResolveInfo resolveInfo = i.next(); -// if (isEnabledLP(resolveInfo.activityInfo)) { -// retList.add(resolveInfo); -// } -// } -// return retList; -// } - // Keys are String (activity class name), values are Activity. private final ArrayMap<ComponentName, PackageParser.Activity> mActivities = new ArrayMap<ComponentName, PackageParser.Activity>(); @@ -11290,8 +11645,8 @@ public class PackageManagerService extends IPackageManager.Stub { * @return the current "allow unknown sources" setting */ private int getUnknownSourcesSettings() { - return android.provider.Settings.Global.getInt(mContext.getContentResolver(), - android.provider.Settings.Global.INSTALL_NON_MARKET_APPS, + return android.provider.Settings.Secure.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, -1); } @@ -12255,8 +12610,6 @@ public class PackageManagerService extends IPackageManager.Stub { * Called after the source arguments are copied. This is used mostly for * MoveParams when it needs to read the source file to put it in the * destination. - * - * @return */ int doPostCopy(int uid) { return PackageManager.INSTALL_SUCCEEDED; diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index b501398c7d57..bae628ab1892 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -39,6 +39,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -1213,6 +1214,8 @@ final class AccessibilityController { window.type = windowState.mAttrs.type; window.layer = windowState.mLayer; window.token = windowState.mClient.asBinder(); + window.title = windowState.mAttrs.getTitle(); + window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor; WindowState attachedWindow = windowState.mAttachedWindow; if (attachedWindow != null) { @@ -1285,6 +1288,12 @@ final class AccessibilityController { && !oldWindow.childTokens.equals(newWindow.childTokens)) { return true; } + if (!TextUtils.equals(oldWindow.title, newWindow.title)) { + return true; + } + if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { + return true; + } return false; } diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java index 97d0ae0f8546..3ec02b9f66fc 100644 --- a/services/core/java/com/android/server/wm/DimLayerController.java +++ b/services/core/java/com/android/server/wm/DimLayerController.java @@ -183,7 +183,12 @@ class DimLayerController { for (int i = mState.size() - 1; i >= 0; i--) { DimLayer.DimLayerUser user = mState.keyAt(i); - if (user.isFullscreen()) { + DimLayerState state = mState.valueAt(i); + // We have to check that we are acutally the shared fullscreen layer + // for this path. If we began as non fullscreen and became fullscreen + // (e.g. Docked stack closing), then we may not be the shared layer + // and we have to make sure we always animate the layer. + if (user.isFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) { fullScreen = i; if (mState.valueAt(i).continueDimming) { fullScreenAndDimming = i; diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java index f76f03fdc0fe..2437ff5a6e88 100644 --- a/services/core/java/com/android/server/wm/WindowLayersController.java +++ b/services/core/java/com/android/server/wm/WindowLayersController.java @@ -203,6 +203,12 @@ public class WindowLayersController { // the divider sometimes overlaps the app windows. layer++; layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer); + + // If we have a dock divider ensure the Input Method is above it. + if (mDockDivider != null && mService.mInputMethodWindow != null) { + layer = assignAndIncreaseLayerIfNeeded(mService.mInputMethodWindow, layer); + } + // We know that we will be animating a relaunching window in the near future, which will // receive a z-order increase. We want the replaced window to immediately receive the same // treatment, e.g. to be above the dock divider. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 14ae74f3967d..fbe28038028b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1373,7 +1373,7 @@ public class WindowManagerService extends IWindowManager.Stub // needs to sit above the dock divider, so it doesn't get cut in half. We make the dock // divider be a target for IME, so this relationship can occur naturally. if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) - || type == TYPE_APPLICATION_STARTING || type == TYPE_DOCK_DIVIDER) { + || type == TYPE_APPLICATION_STARTING) { if (DEBUG_INPUT_METHOD) { Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding()); if (!w.isVisibleOrAdding()) { @@ -4135,6 +4135,14 @@ public class WindowManagerService extends IWindowManager.Stub for (int i = 0; i < windowsCount; i++) { WindowState win = wtoken.allAppWindows.get(i); if (win == wtoken.startingWindow) { + // Starting window that's exiting will be removed when the animation + // finishes. Mark all relevant flags for that finishExit will proceed + // all the way to actually remove it. + if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) { + win.mAnimatingExit = true; + win.mRemoveOnExit = true; + win.mWindowRemovalAllowed = true; + } continue; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 57ead8b3a617..f0f292ace789 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -670,10 +670,18 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContainingFrame.bottom = mContainingFrame.top + frozen.height(); } final WindowState imeWin = mService.mInputMethodWindow; - if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this - && mContainingFrame.bottom > cf.bottom) { - // IME is up and obscuring this window. Adjust the window position so it is visible. - mContainingFrame.top -= mContainingFrame.bottom - cf.bottom; + // IME is up and obscuring this window. Adjust the window position so it is visible. + if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) { + if (windowsAreFloating && mContainingFrame.bottom > cf.bottom) { + // In freeform we want to move the top up directly. + // TODO: Investigate why this is cf not pf. + mContainingFrame.top -= mContainingFrame.bottom - cf.bottom; + } else if (mContainingFrame.bottom > pf.bottom) { + // But in docked we want to behave like fullscreen + // and behave as if the task were given smaller bounds + // for the purposes of layout. + mContainingFrame.bottom = pf.bottom; + } } if (windowsAreFloating) { @@ -1878,6 +1886,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { } private boolean shouldSaveSurface() { + if (mWinAnimator.mSurfaceController == null) { + // Don't bother if the surface controller is gone for any reason. + return false; + } + if ((mAttrs.flags & FLAG_SECURE) != 0) { // We don't save secure surfaces since their content shouldn't be shown while the app // isn't on screen and content might leak through during the transition animation with @@ -1951,10 +1964,18 @@ final class WindowState implements WindowManagerPolicy.WindowState { return; } mSurfaceSaved = false; - setHasSurface(true); - mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW; - if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { - Slog.v(TAG, "Restoring saved surface: " + this); + if (mWinAnimator.mSurfaceController != null) { + setHasSurface(true); + mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW; + + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG, "Restoring saved surface: " + this); + } + } else { + // mSurfaceController shouldn't be null if mSurfaceSaved was still true at + // this point. Even if we destroyed the saved surface because of rotation + // or resize, mSurfaceSaved flag should have been cleared. So this is a wtf. + Slog.wtf(TAG, "Failed to restore saved surface: surface gone! " + this); } } diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 641c34bd2dda..d631f3531127 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -33,7 +33,7 @@ static const char* kNoCompressExt[] = { ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", ".webm" + ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv" }; /* fwd decls, so I can write this downward */ |