diff options
31 files changed, 814 insertions, 178 deletions
@@ -550,6 +550,8 @@ aidl_files := \ frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \ frameworks/base/core/java/android/speech/tts/Voice.aidl \ frameworks/base/core/java/android/app/usage/UsageEvents.aidl \ + frameworks/base/core/java/android/app/AssistStructure.aidl \ + frameworks/base/core/java/android/app/AssistContent.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ frameworks/base/core/java/android/app/WallpaperInfo.aidl \ frameworks/base/core/java/android/app/AppOpsManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 4c24114..13e6dc0 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3429,6 +3429,7 @@ package android.app { method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder); method public boolean onPrepareOptionsMenu(android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideAssistContent(android.app.AssistContent); method public void onProvideAssistData(android.os.Bundle); method protected void onRestart(); method protected void onRestoreInstanceState(android.os.Bundle); @@ -3957,20 +3958,33 @@ package android.app { field public java.lang.String serviceDetails; } - public final class AssistData implements android.os.Parcelable { + public class AssistContent implements android.os.Parcelable { + ctor public AssistContent(); + method public int describeContents(); + method public static android.app.AssistContent getAssistContent(android.os.Bundle); + method public android.content.ClipData getClipData(); + method public android.content.Intent getIntent(); + method public void setClipData(android.content.ClipData); + method public void setIntent(android.content.Intent); + method public void writeToParcel(android.os.Parcel, int); + field public static final java.lang.String ASSIST_KEY = "android:assist_content"; + field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR; + } + + public final class AssistStructure implements android.os.Parcelable { method public int describeContents(); method public android.content.ComponentName getActivityComponent(); - method public static android.app.AssistData getAssistData(android.os.Bundle); - method public void getWindowAt(int, android.app.AssistData.ViewNode); + method public static android.app.AssistStructure getAssistStructure(android.os.Bundle); + method public void getWindowAt(int, android.app.AssistStructure.ViewNode); method public int getWindowCount(); method public void writeToParcel(android.os.Parcel, int); - field public static final java.lang.String ASSIST_KEY = "android:assist"; - field public static final android.os.Parcelable.Creator<android.app.AssistData> CREATOR; + field public static final java.lang.String ASSIST_KEY = "android:assist_structure"; + field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR; } - public static class AssistData.ViewNode { - ctor public AssistData.ViewNode(); - method public void getChildAt(int, android.app.AssistData.ViewNode); + public static class AssistStructure.ViewNode { + ctor public AssistStructure.ViewNode(); + method public void getChildAt(int, android.app.AssistStructure.ViewNode); method public int getChildCount(); method public java.lang.String getClassName(); method public java.lang.String getContentDescription(); @@ -3980,9 +3994,13 @@ package android.app { method public int getLeft(); method public int getScrollX(); method public int getScrollY(); - method public java.lang.String getText(); + method public java.lang.CharSequence getText(); + method public int getTextBackgroundColor(); + method public int getTextColor(); method public int getTextSelectionEnd(); method public int getTextSelectionStart(); + method public float getTextSize(); + method public int getTextStyle(); method public int getTop(); method public int getVisibility(); method public int getWidth(); @@ -3996,6 +4014,11 @@ package android.app { method public boolean isFocused(); method public boolean isLongClickable(); method public boolean isSelected(); + field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 + field public static final int TEXT_STYLE_BOLD = 1; // 0x1 + field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 + field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 + field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 } public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { @@ -7920,6 +7943,7 @@ package android.content { field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; + field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID"; field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; @@ -34301,7 +34325,7 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public void onProvideAssistData(android.view.ViewAssistData, android.os.Bundle); + method public void onProvideAssistStructure(android.view.ViewAssistStructure, android.os.Bundle); method protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); method protected android.os.Parcelable onSaveInstanceState(); @@ -34704,8 +34728,8 @@ package android.view { method public static android.animation.Animator createCircularReveal(android.view.View, int, int, float, float); } - public abstract class ViewAssistData { - ctor public ViewAssistData(); + public abstract class ViewAssistStructure { + ctor public ViewAssistStructure(); method public abstract java.lang.CharSequence getHint(); method public abstract java.lang.CharSequence getText(); method public abstract int getTextSelectionEnd(); @@ -34713,6 +34737,7 @@ package android.view { method public abstract void setHint(java.lang.CharSequence); method public abstract void setText(java.lang.CharSequence); method public abstract void setText(java.lang.CharSequence, int, int); + method public abstract void setTextPaint(android.text.TextPaint); } public class ViewConfiguration { diff --git a/api/system-current.txt b/api/system-current.txt index d641bd9..0e1ccb1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3512,6 +3512,7 @@ package android.app { method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder); method public boolean onPrepareOptionsMenu(android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideAssistContent(android.app.AssistContent); method public void onProvideAssistData(android.os.Bundle); method protected void onRestart(); method protected void onRestoreInstanceState(android.os.Bundle); @@ -4047,20 +4048,33 @@ package android.app { field public java.lang.String serviceDetails; } - public final class AssistData implements android.os.Parcelable { + public class AssistContent implements android.os.Parcelable { + ctor public AssistContent(); + method public int describeContents(); + method public static android.app.AssistContent getAssistContent(android.os.Bundle); + method public android.content.ClipData getClipData(); + method public android.content.Intent getIntent(); + method public void setClipData(android.content.ClipData); + method public void setIntent(android.content.Intent); + method public void writeToParcel(android.os.Parcel, int); + field public static final java.lang.String ASSIST_KEY = "android:assist_content"; + field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR; + } + + public final class AssistStructure implements android.os.Parcelable { method public int describeContents(); method public android.content.ComponentName getActivityComponent(); - method public static android.app.AssistData getAssistData(android.os.Bundle); - method public void getWindowAt(int, android.app.AssistData.ViewNode); + method public static android.app.AssistStructure getAssistStructure(android.os.Bundle); + method public void getWindowAt(int, android.app.AssistStructure.ViewNode); method public int getWindowCount(); method public void writeToParcel(android.os.Parcel, int); - field public static final java.lang.String ASSIST_KEY = "android:assist"; - field public static final android.os.Parcelable.Creator<android.app.AssistData> CREATOR; + field public static final java.lang.String ASSIST_KEY = "android:assist_structure"; + field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR; } - public static class AssistData.ViewNode { - ctor public AssistData.ViewNode(); - method public void getChildAt(int, android.app.AssistData.ViewNode); + public static class AssistStructure.ViewNode { + ctor public AssistStructure.ViewNode(); + method public void getChildAt(int, android.app.AssistStructure.ViewNode); method public int getChildCount(); method public java.lang.String getClassName(); method public java.lang.String getContentDescription(); @@ -4070,9 +4084,13 @@ package android.app { method public int getLeft(); method public int getScrollX(); method public int getScrollY(); - method public java.lang.String getText(); + method public java.lang.CharSequence getText(); + method public int getTextBackgroundColor(); + method public int getTextColor(); method public int getTextSelectionEnd(); method public int getTextSelectionStart(); + method public float getTextSize(); + method public int getTextStyle(); method public int getTop(); method public int getVisibility(); method public int getWidth(); @@ -4086,6 +4104,11 @@ package android.app { method public boolean isFocused(); method public boolean isLongClickable(); method public boolean isSelected(); + field public static final int TEXT_COLOR_UNDEFINED = 1; // 0x1 + field public static final int TEXT_STYLE_BOLD = 1; // 0x1 + field public static final int TEXT_STYLE_ITALIC = 2; // 0x2 + field public static final int TEXT_STYLE_STRIKE_THRU = 8; // 0x8 + field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4 } public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { @@ -8134,6 +8157,7 @@ package android.content { field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT"; field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; + field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID"; field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; @@ -36469,7 +36493,7 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); - method public void onProvideAssistData(android.view.ViewAssistData, android.os.Bundle); + method public void onProvideAssistStructure(android.view.ViewAssistStructure, android.os.Bundle); method protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); method protected android.os.Parcelable onSaveInstanceState(); @@ -36872,8 +36896,8 @@ package android.view { method public static android.animation.Animator createCircularReveal(android.view.View, int, int, float, float); } - public abstract class ViewAssistData { - ctor public ViewAssistData(); + public abstract class ViewAssistStructure { + ctor public ViewAssistStructure(); method public abstract java.lang.CharSequence getHint(); method public abstract java.lang.CharSequence getText(); method public abstract int getTextSelectionEnd(); @@ -36881,6 +36905,7 @@ package android.view { method public abstract void setHint(java.lang.CharSequence); method public abstract void setText(java.lang.CharSequence); method public abstract void setText(java.lang.CharSequence, int, int); + method public abstract void setTextPaint(android.text.TextPaint); } public class ViewConfiguration { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9f8befe..02f33b4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1547,17 +1547,35 @@ public class Activity extends ContextThemeWrapper * {@link Intent#ACTION_ASSIST} Intent with all of the context of the current * application. You can override this method to place into the bundle anything * you would like to appear in the {@link Intent#EXTRA_ASSIST_CONTEXT} part - * of the assist Intent. The default implementation automatically generates a - * {@link AssistData} from your activity and places it in to the Bundle; if you - * don't want your UI reported to the assistant, don't call this default - * implementation. + * of the assist Intent. * * <p>This function will be called after any global assist callbacks that had * been registered with {@link Application#registerOnProvideAssistDataListener * Application.registerOnProvideAssistDataListener}. */ public void onProvideAssistData(Bundle data) { - data.putParcelable(AssistData.ASSIST_KEY, new AssistData(this)); + } + + /** + * This is called when the user is requesting an assist, to provide references + * to content related to the current activity. Before being called, the + * {@code outContent} Intent is filled with the base Intent of the activity (the Intent + * returned by {@link #getIntent()}). The Intent's extras are stripped of any types + * that are not valid for {@link PersistableBundle} or non-framework Parcelables, and + * the flags {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION} and + * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} are cleared from the Intent. + * + * <p>Custom implementation may adjust the content intent to better reflect the top-level + * context of the activity, and fill in its ClipData with additional content of + * interest that the user is currently viewing. For example, an image gallery application + * that has launched in to an activity allowing the user to swipe through pictures should + * modify the intent to reference the current image they are looking it; such an + * application when showing a list of pictures should add a ClipData that has + * references to all of the pictures currently visible on screen.</p> + * + * @param outContent The assist content to return. + */ + public void onProvideAssistContent(AssistContent outContent) { } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c525ef2..29b024ac 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -309,6 +309,12 @@ public class ActivityManager { /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 13; + /** @hide requestType for assist context: only basic information. */ + public static final int ASSIST_CONTEXT_BASIC = 0; + + /** @hide requestType for assist context: generate full AssistStructure. */ + public static final int ASSIST_CONTEXT_FULL = 1; + /** * Lock task mode is not active. */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 97793c5..7b8ec74 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2519,6 +2519,17 @@ public final class ActivityThread { if (r != null) { r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); r.activity.onProvideAssistData(data); + if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) { + data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity)); + AssistContent content = new AssistContent(); + Intent intent = new Intent(r.activity.getIntent()); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + intent.removeUnsafeExtras(); + content.setIntent(intent); + r.activity.onProvideAssistContent(content); + data.putParcelable(AssistContent.ASSIST_KEY, content); + } } if (data.isEmpty()) { data = null; diff --git a/core/java/android/app/AssistContent.aidl b/core/java/android/app/AssistContent.aidl new file mode 100644 index 0000000..a6321bf --- /dev/null +++ b/core/java/android/app/AssistContent.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +parcelable AssistContent; diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java new file mode 100644 index 0000000..ace4af7 --- /dev/null +++ b/core/java/android/app/AssistContent.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.content.ClipData; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Holds information about the content an application is viewing, to hand to an + * assistant at the user's request. This is filled in by + * {@link Activity#onProvideAssistContent Activity.onProvideAssistContent}. + */ +public class AssistContent implements Parcelable { + private Intent mIntent; + private ClipData mClipData; + + /** + * Key name this data structure is stored in the Bundle generated by + * {@link Activity#onProvideAssistData}. + */ + public static final String ASSIST_KEY = "android:assist_content"; + + /** + * Retrieve the framework-generated AssistContent that is stored within + * the Bundle filled in by {@link Activity#onProvideAssistContent}. + */ + public static AssistContent getAssistContent(Bundle assistBundle) { + return assistBundle.getParcelable(ASSIST_KEY); + } + + public AssistContent() { + } + + /** + * Sets the Intent associated with the content, describing the current top-level context of + * the activity. If this contains a reference to a piece of data related to the activity, + * be sure to set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} so the accessibilty + * service can access it. + */ + public void setIntent(Intent intent) { + mIntent = intent; + } + + /** + * Return the current {@link #setIntent}, which you can modify in-place. + */ + public Intent getIntent() { + return mIntent; + } + + /** + * Optional additional content items that are involved with + * the current UI. Access to this content will be granted to the assistant as if you + * are sending it through an Intent with {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}. + */ + public void setClipData(ClipData clip) { + mClipData = clip; + } + + /** + * Return the current {@link #setClipData}, which you can modify in-place. + */ + public ClipData getClipData() { + return mClipData; + } + + AssistContent(Parcel in) { + if (in.readInt() != 0) { + mIntent = Intent.CREATOR.createFromParcel(in); + } + if (in.readInt() != 0) { + mClipData = ClipData.CREATOR.createFromParcel(in); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mIntent != null) { + dest.writeInt(1); + mIntent.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + if (mClipData != null) { + dest.writeInt(1); + mClipData.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + } + + public static final Parcelable.Creator<AssistContent> CREATOR + = new Parcelable.Creator<AssistContent>() { + public AssistContent createFromParcel(Parcel in) { + return new AssistContent(in); + } + + public AssistContent[] newArray(int size) { + return new AssistContent[size]; + } + }; +} diff --git a/core/java/android/app/AssistStructure.aidl b/core/java/android/app/AssistStructure.aidl new file mode 100644 index 0000000..07fb2453 --- /dev/null +++ b/core/java/android/app/AssistStructure.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +parcelable AssistStructure; diff --git a/core/java/android/app/AssistData.java b/core/java/android/app/AssistStructure.java index 7b5eb6d..25153fc 100644 --- a/core/java/android/app/AssistData.java +++ b/core/java/android/app/AssistStructure.java @@ -17,13 +17,19 @@ package android.app; import android.content.ComponentName; +import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.Typeface; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.PooledStringReader; +import android.os.PooledStringWriter; +import android.text.TextPaint; +import android.text.TextUtils; import android.util.Log; import android.view.View; -import android.view.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; @@ -34,28 +40,32 @@ import java.util.ArrayList; /** * Assist data automatically created by the platform's implementation * of {@link Activity#onProvideAssistData}. Retrieve it from the assist - * data with {@link #getAssistData(android.os.Bundle)}. + * data with {@link #getAssistStructure(android.os.Bundle)}. */ -final public class AssistData implements Parcelable { - static final String TAG = "AssistData"; +final public class AssistStructure implements Parcelable { + static final String TAG = "AssistStructure"; /** * Key name this data structure is stored in the Bundle generated by * {@link Activity#onProvideAssistData}. */ - public static final String ASSIST_KEY = "android:assist"; + public static final String ASSIST_KEY = "android:assist_structure"; final ComponentName mActivityComponent; final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>(); - ViewAssistDataImpl mTmpViewAssistDataImpl = new ViewAssistDataImpl(); + ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); Bundle mTmpExtras = new Bundle(); - final static class ViewAssistDataImpl extends ViewAssistData { + final static class ViewAssistStructureImpl extends ViewAssistStructure { CharSequence mText; int mTextSelectionStart = -1; int mTextSelectionEnd = -1; + int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED; + int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED; + float mTextSize = 0; + int mTextStyle = 0; CharSequence mHint; @Override @@ -72,6 +82,33 @@ final public class AssistData implements Parcelable { } @Override + public void setTextPaint(TextPaint paint) { + mTextColor = paint.getColor(); + mTextBackgroundColor = paint.bgColor; + mTextSize = paint.getTextSize(); + mTextStyle = 0; + Typeface tf = paint.getTypeface(); + if (tf != null) { + if (tf.isBold()) { + mTextStyle |= ViewNode.TEXT_STYLE_BOLD; + } + if (tf.isItalic()) { + mTextStyle |= ViewNode.TEXT_STYLE_ITALIC; + } + } + int pflags = paint.getFlags(); + if ((pflags& Paint.FAKE_BOLD_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_BOLD; + } + if ((pflags& Paint.UNDERLINE_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_UNDERLINE; + } + if ((pflags& Paint.STRIKE_THRU_TEXT_FLAG) != 0) { + mTextStyle |= ViewNode.TEXT_STYLE_STRIKE_THRU; + } + } + + @Override public void setHint(CharSequence hint) { mHint = hint; } @@ -98,29 +135,45 @@ final public class AssistData implements Parcelable { } final static class ViewNodeTextImpl { - final String mText; + final CharSequence mText; final int mTextSelectionStart; final int mTextSelectionEnd; + int mTextColor; + int mTextBackgroundColor; + float mTextSize; + int mTextStyle; final String mHint; - ViewNodeTextImpl(ViewAssistDataImpl data) { - mText = data.mText != null ? data.mText.toString() : null; + ViewNodeTextImpl(ViewAssistStructureImpl data) { + mText = data.mText; mTextSelectionStart = data.mTextSelectionStart; mTextSelectionEnd = data.mTextSelectionEnd; + mTextColor = data.mTextColor; + mTextBackgroundColor = data.mTextBackgroundColor; + mTextSize = data.mTextSize; + mTextStyle = data.mTextStyle; mHint = data.mHint != null ? data.mHint.toString() : null; } ViewNodeTextImpl(Parcel in) { - mText = in.readString(); + mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mTextSelectionStart = in.readInt(); mTextSelectionEnd = in.readInt(); + mTextColor = in.readInt(); + mTextBackgroundColor = in.readInt(); + mTextSize = in.readFloat(); + mTextStyle = in.readInt(); mHint = in.readString(); } void writeToParcel(Parcel out) { - out.writeString(mText); + TextUtils.writeToParcel(mText, out, 0); out.writeInt(mTextSelectionStart); out.writeInt(mTextSelectionEnd); + out.writeInt(mTextColor); + out.writeInt(mTextBackgroundColor); + out.writeFloat(mTextSize); + out.writeInt(mTextStyle); out.writeString(mHint); } } @@ -155,7 +208,7 @@ final public class AssistData implements Parcelable { final ViewNodeImpl[] mChildren; - ViewNodeImpl(AssistData assistData, View view, int left, int top, + ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top, CharSequence contentDescription) { mX = left; mY = top; @@ -197,18 +250,18 @@ final public class AssistData implements Parcelable { mFlags = flags; mClassName = view.getAccessibilityClassName().toString(); mContentDescription = contentDescription != null ? contentDescription.toString() : null; - final ViewAssistDataImpl viewData = assistData.mTmpViewAssistDataImpl; - final Bundle extras = assistData.mTmpExtras; - view.onProvideAssistData(viewData, extras); + final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl; + final Bundle extras = assistStructure.mTmpExtras; + view.onProvideAssistStructure(viewData, extras); if (viewData.mText != null || viewData.mHint != null) { mText = new ViewNodeTextImpl(viewData); - assistData.mTmpViewAssistDataImpl = new ViewAssistDataImpl(); + assistStructure.mTmpViewAssistStructureImpl = new ViewAssistStructureImpl(); } else { mText = null; } if (!extras.isEmpty()) { mExtras = extras; - assistData.mTmpExtras = new Bundle(); + assistStructure.mTmpExtras = new Bundle(); } else { mExtras = null; } @@ -218,7 +271,7 @@ final public class AssistData implements Parcelable { if (NCHILDREN > 0) { mChildren = new ViewNodeImpl[NCHILDREN]; for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNodeImpl(assistData, vg.getChildAt(i)); + mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i)); } } else { mChildren = null; @@ -228,11 +281,11 @@ final public class AssistData implements Parcelable { } } - ViewNodeImpl(AssistData assistData, View view) { - this(assistData, view, view.getLeft(), view.getTop(), view.getContentDescription()); + ViewNodeImpl(AssistStructure assistStructure, View view) { + this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription()); } - ViewNodeImpl(Parcel in) { + ViewNodeImpl(Parcel in, PooledStringReader preader) { mX = in.readInt(); mY = in.readInt(); mScrollX = in.readInt(); @@ -240,7 +293,7 @@ final public class AssistData implements Parcelable { mWidth = in.readInt(); mHeight = in.readInt(); mFlags = in.readInt(); - mClassName = in.readString(); + mClassName = preader.readString(); mContentDescription = in.readString(); if (in.readInt() != 0) { mText = new ViewNodeTextImpl(in); @@ -252,14 +305,14 @@ final public class AssistData implements Parcelable { if (NCHILDREN > 0) { mChildren = new ViewNodeImpl[NCHILDREN]; for (int i=0; i<NCHILDREN; i++) { - mChildren[i] = new ViewNodeImpl(in); + mChildren[i] = new ViewNodeImpl(in, preader); } } else { mChildren = null; } } - void writeToParcel(Parcel out) { + void writeToParcel(Parcel out, PooledStringWriter pwriter) { out.writeInt(mX); out.writeInt(mY); out.writeInt(mScrollX); @@ -267,7 +320,7 @@ final public class AssistData implements Parcelable { out.writeInt(mWidth); out.writeInt(mHeight); out.writeInt(mFlags); - out.writeString(mClassName); + pwriter.writeString(mClassName); out.writeString(mContentDescription); if (mText != null) { out.writeInt(1); @@ -280,7 +333,7 @@ final public class AssistData implements Parcelable { final int NCHILDREN = mChildren.length; out.writeInt(NCHILDREN); for (int i=0; i<NCHILDREN; i++) { - mChildren[i].writeToParcel(out); + mChildren[i].writeToParcel(out, pwriter); } } else { out.writeInt(0); @@ -292,6 +345,17 @@ final public class AssistData implements Parcelable { * Provides access to information about a single view in the assist data. */ static public class ViewNode { + /** + * Magic value for text color that has not been defined, which is very unlikely + * to be confused with a real text color. + */ + public static final int TEXT_COLOR_UNDEFINED = 1; + + public static final int TEXT_STYLE_BOLD = 1<<0; + public static final int TEXT_STYLE_ITALIC = 1<<1; + public static final int TEXT_STYLE_UNDERLINE = 1<<2; + public static final int TEXT_STYLE_STRIKE_THRU = 1<<3; + ViewNodeImpl mImpl; public ViewNode() { @@ -373,7 +437,7 @@ final public class AssistData implements Parcelable { return mImpl.mContentDescription; } - public String getText() { + public CharSequence getText() { return mImpl.mText != null ? mImpl.mText.mText : null; } @@ -385,6 +449,22 @@ final public class AssistData implements Parcelable { return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1; } + public int getTextColor() { + return mImpl.mText != null ? mImpl.mText.mTextColor : TEXT_COLOR_UNDEFINED; + } + + public int getTextBackgroundColor() { + return mImpl.mText != null ? mImpl.mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED; + } + + public float getTextSize() { + return mImpl.mText != null ? mImpl.mText.mTextSize : 0; + } + + public int getTextStyle() { + return mImpl.mText != null ? mImpl.mText.mTextStyle : 0; + } + public String getHint() { return mImpl.mText != null ? mImpl.mText.mHint : null; } @@ -402,7 +482,7 @@ final public class AssistData implements Parcelable { } } - AssistData(Activity activity) { + AssistStructure(Activity activity) { mActivityComponent = activity.getComponentName(); ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( activity.getActivityToken()); @@ -417,11 +497,12 @@ final public class AssistData implements Parcelable { } } - AssistData(Parcel in) { + AssistStructure(Parcel in) { + PooledStringReader preader = new PooledStringReader(in); mActivityComponent = ComponentName.readFromParcel(in); final int N = in.readInt(); for (int i=0; i<N; i++) { - mRootViews.add(new ViewNodeImpl(in)); + mRootViews.add(new ViewNodeImpl(in, preader)); } //dump(); } @@ -450,10 +531,14 @@ final public class AssistData implements Parcelable { if (contentDescription != null) { Log.i(TAG, prefix + " Content description: " + contentDescription); } - String text = node.getText(); + CharSequence text = node.getText(); if (text != null) { Log.i(TAG, prefix + " Text (sel " + node.getTextSelectionStart() + "-" + node.getTextSelectionEnd() + "): " + text); + Log.i(TAG, prefix + " Text size: " + node.getTextSize() + " , style: #" + + node.getTextStyle()); + Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor()) + + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor())); } String hint = node.getHint(); if (hint != null) { @@ -476,10 +561,10 @@ final public class AssistData implements Parcelable { } /** - * Retrieve the framework-generated AssistData that is stored within + * Retrieve the framework-generated AssistStructure that is stored within * the Bundle filled in by {@link Activity#onProvideAssistData}. */ - public static AssistData getAssistData(Bundle assistBundle) { + public static AssistStructure getAssistStructure(Bundle assistBundle) { return assistBundle.getParcelable(ASSIST_KEY); } @@ -509,23 +594,25 @@ final public class AssistData implements Parcelable { public void writeToParcel(Parcel out, int flags) { int start = out.dataPosition(); + PooledStringWriter pwriter = new PooledStringWriter(out); ComponentName.writeToParcel(mActivityComponent, out); final int N = mRootViews.size(); out.writeInt(N); for (int i=0; i<N; i++) { - mRootViews.get(i).writeToParcel(out); + mRootViews.get(i).writeToParcel(out, pwriter); } + pwriter.finish(); Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes"); } - public static final Parcelable.Creator<AssistData> CREATOR - = new Parcelable.Creator<AssistData>() { - public AssistData createFromParcel(Parcel in) { - return new AssistData(in); + public static final Parcelable.Creator<AssistStructure> CREATOR + = new Parcelable.Creator<AssistStructure>() { + public AssistStructure createFromParcel(Parcel in) { + return new AssistStructure(in); } - public AssistData[] newArray(int size) { - return new AssistData[size]; + public AssistStructure[] newArray(int size) { + return new AssistStructure[size]; } }; } diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl index 03e7ff4..6d27910 100644 --- a/core/java/android/app/ISearchManager.aidl +++ b/core/java/android/app/ISearchManager.aidl @@ -31,5 +31,5 @@ interface ISearchManager { ComponentName getGlobalSearchActivity(); ComponentName getWebSearchActivity(); ComponentName getAssistIntent(int userHandle); - boolean launchAssistAction(int requestType, String hint, int userHandle); + boolean launchAssistAction(String hint, int userHandle); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index d7c4467..fa27631 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -969,7 +969,7 @@ public class SearchManager intent.setComponent(comp); if (inclContext) { IActivityManager am = ActivityManagerNative.getDefault(); - Bundle extras = am.getAssistContextExtras(0); + Bundle extras = am.getAssistContextExtras(ActivityManager.ASSIST_CONTEXT_BASIC); if (extras != null) { intent.replaceExtras(extras); } @@ -985,12 +985,12 @@ public class SearchManager * Launch an assist action for the current top activity. * @hide */ - public boolean launchAssistAction(int requestType, String hint, int userHandle) { + public boolean launchAssistAction(String hint, int userHandle) { try { if (mService == null) { return false; } - return mService.launchAssistAction(requestType, hint, userHandle); + return mService.launchAssistAction(hint, userHandle); } catch (RemoteException re) { Log.e(TAG, "launchAssistAction() failed: " + re); return false; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3222b2b..2ed8c44 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1239,6 +1239,13 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.ASSIST_PACKAGE"; /** + * An optional field on {@link #ACTION_ASSIST} containing the uid of the current foreground + * application package at the time the assist was invoked. + */ + public static final String EXTRA_ASSIST_UID + = "android.intent.extra.ASSIST_UID"; + + /** * An optional field on {@link #ACTION_ASSIST} and containing additional contextual * information supplied by the current foreground app at the time of the assist request. * This is a {@link Bundle} of additional data. @@ -5386,6 +5393,16 @@ public class Intent implements Parcelable, Cloneable { } /** + * Filter extras to only basic types. + * @hide + */ + public void removeUnsafeExtras() { + if (mExtras != null) { + mExtras.filterValues(); + } + } + + /** * Retrieve any special flags associated with this intent. You will * normally just set them with {@link #setFlags} and let the system * take the appropriate action with them. diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 7f8c95b..c373308 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -22,7 +22,6 @@ import android.util.Log; import java.io.Serializable; import java.util.ArrayList; -import java.util.Map; import java.util.Set; /** @@ -309,7 +308,7 @@ public class BaseBundle { * * @param map a Map */ - void putAll(Map map) { + void putAll(ArrayMap map) { unparcel(); mMap.putAll(map); } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index c20024a..5e9b8c1 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -252,6 +252,29 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Filter values in Bundle to only basic types. + * @hide + */ + public void filterValues() { + unparcel(); + if (mMap != null) { + for (int i = mMap.size() - 1; i >= 0; i--) { + Object value = mMap.valueAt(i); + if (PersistableBundle.isValidType(value)) { + continue; + } + if (value instanceof Bundle) { + ((Bundle)value).filterValues(); + } + if (value.getClass().getName().startsWith("android.")) { + continue; + } + mMap.removeAt(i); + } + } + } + + /** * Inserts a byte value into the mapping of this Bundle, replacing * any existing value for the given key. * diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index a467c87..ea180b2 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -45,6 +45,16 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL; } + /** @hide */ + public static boolean isValidType(Object value) { + return (value instanceof Integer) || (value instanceof Long) || + (value instanceof Double) || (value instanceof String) || + (value instanceof int[]) || (value instanceof long[]) || + (value instanceof double[]) || (value instanceof String[]) || + (value instanceof PersistableBundle) || (value == null) || + (value instanceof Boolean) || (value instanceof boolean[]); + } + /** * Constructs a new, empty PersistableBundle. */ @@ -78,29 +88,22 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * @param map a Map containing only those items that can be persisted. * @throws IllegalArgumentException if any element of #map cannot be persisted. */ - private PersistableBundle(Map<String, Object> map) { + private PersistableBundle(ArrayMap<String, Object> map) { super(); // First stuff everything in. putAll(map); // Now verify each item throwing an exception if there is a violation. - Set<String> keys = map.keySet(); - Iterator<String> iterator = keys.iterator(); - while (iterator.hasNext()) { - String key = iterator.next(); - Object value = map.get(key); - if (value instanceof Map) { + final int N = mMap.size(); + for (int i=0; i<N; i++) { + Object value = mMap.valueAt(i); + if (value instanceof ArrayMap) { // Fix up any Maps by replacing them with PersistableBundles. - putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value)); - } else if (!(value instanceof Integer) && !(value instanceof Long) && - !(value instanceof Double) && !(value instanceof String) && - !(value instanceof int[]) && !(value instanceof long[]) && - !(value instanceof double[]) && !(value instanceof String[]) && - !(value instanceof PersistableBundle) && (value != null) && - !(value instanceof Boolean) && !(value instanceof boolean[])) { - throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key + - " value=" + value); + mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value)); + } else if (!isValidType(value)) { + throw new IllegalArgumentException("Bad value in PersistableBundle key=" + + mMap.keyAt(i) + " value=" + value); } } } @@ -242,8 +245,9 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { if (event == XmlPullParser.START_TAG) { - return new PersistableBundle((Map<String, Object>) - XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback())); + return new PersistableBundle((ArrayMap<String, Object>) + XmlUtils.readThisArrayMapXml(in, startTag, tagName, + new MyReadMapCallback())); } } return EMPTY; diff --git a/core/java/android/os/PooledStringReader.java b/core/java/android/os/PooledStringReader.java new file mode 100644 index 0000000..7795957 --- /dev/null +++ b/core/java/android/os/PooledStringReader.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Helper class for reading pooling strings from a Parcel. It must be used + * in conjunction with {@link android.os.PooledStringWriter}. This really needs + * to be pushed in to Parcel itself, but doing that is... complicated. + * @hide + */ +public class PooledStringReader { + private final Parcel mIn; + + /** + * The pool of strings we have collected so far. + */ + private final String[] mPool; + + public PooledStringReader(Parcel in) { + mIn = in; + final int size = in.readInt(); + mPool = new String[size]; + } + + public String readString() { + int idx = mIn.readInt(); + if (idx >= 0) { + return mPool[idx]; + } else { + idx = (-idx) - 1; + String str = mIn.readString(); + mPool[idx] = str; + return str; + } + } +} diff --git a/core/java/android/os/PooledStringWriter.java b/core/java/android/os/PooledStringWriter.java new file mode 100644 index 0000000..eac297d --- /dev/null +++ b/core/java/android/os/PooledStringWriter.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import java.util.HashMap; + +/** + * Helper class for writing pooled strings into a Parcel. It must be used + * in conjunction with {@link android.os.PooledStringReader}. This really needs + * to be pushed in to Parcel itself, but doing that is... complicated. + * @hide + */ +public class PooledStringWriter { + private final Parcel mOut; + + /** + * Book-keeping for writing pooled string objects, mapping strings we have + * written so far to their index in the pool. We deliberately use HashMap + * here since performance is critical, we expect to be doing lots of adds to + * it, and it is only a temporary object so its overall memory footprint is + * not a signifciant issue. + */ + private final HashMap<String, Integer> mPool; + + /** + * Book-keeping for writing pooling string objects, indicating where we + * started writing the pool, which is where we need to ultimately store + * how many strings are in the pool. + */ + private int mStart; + + /** + * Next available index in the pool. + */ + private int mNext; + + public PooledStringWriter(Parcel out) { + mOut = out; + mPool = new HashMap<>(); + mStart = out.dataPosition(); + out.writeInt(0); // reserve space for final pool size. + } + + public void writeString(String str) { + final Integer cur = mPool.get(str); + if (cur != null) { + mOut.writeInt(cur); + } else { + mPool.put(str, mNext); + mOut.writeInt(-(mNext+1)); + mOut.writeString(str); + mNext++; + } + } + + public void finish() { + final int pos = mOut.dataPosition(); + mOut.setDataPosition(mStart); + mOut.writeInt(mNext); + mOut.setDataPosition(pos); + } +} diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java index d4b14f6..cf5affc 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/android/view/PhoneWindow.java @@ -4158,7 +4158,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { // On TVs, if the app doesn't implement search, we want to launch assist. return ((SearchManager)getContext().getSystemService(Context.SEARCH_SERVICE)) - .launchAssistAction(0, null, UserHandle.myUserId()); + .launchAssistAction(null, UserHandle.myUserId()); } return result; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4f2d4a6..69eb7b4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5620,12 +5620,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Called when assist data is being retrieved from a view as part of + * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}. - * @param data - * @param extras + * @param structure Additional standard structured view structure to supply. + * @param extras Non-standard extensions. */ - public void onProvideAssistData(ViewAssistData data, Bundle extras) { + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { } /** diff --git a/core/java/android/view/ViewAssistData.java b/core/java/android/view/ViewAssistStructure.java index 74436ea..5132bb9 100644 --- a/core/java/android/view/ViewAssistData.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -16,13 +16,16 @@ package android.view; +import android.text.TextPaint; + /** - * Container for storing data generated by {@link View#onProvideAssistData - * View.onProvideAssistData}. + * Container for storing additional per-view data generated by {@link View#onProvideAssistStructure + * View.onProvideAssistStructure}. */ -public abstract class ViewAssistData { +public abstract class ViewAssistStructure { public abstract void setText(CharSequence text); public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); + public abstract void setTextPaint(TextPaint paint); public abstract void setHint(CharSequence hint); public abstract CharSequence getText(); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index ce1834e..bb290e7 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -46,7 +46,7 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; -import android.view.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1363,18 +1363,19 @@ public class Switch extends CompoundButton { } @Override - public void onProvideAssistData(ViewAssistData data, Bundle extras) { - super.onProvideAssistData(data, extras); + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { + super.onProvideAssistStructure(structure, extras); CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { - CharSequence oldText = data.getText(); + CharSequence oldText = structure.getText(); if (TextUtils.isEmpty(oldText)) { - data.setText(switchText); + structure.setText(switchText); } else { StringBuilder newText = new StringBuilder(); newText.append(oldText).append(' ').append(switchText); - data.setText(newText); + structure.setText(newText); } + structure.setTextPaint(mTextPaint); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 628833d..447e9ac 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -115,7 +115,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistData; +import android.view.ViewAssistStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; @@ -8580,13 +8580,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void onProvideAssistData(ViewAssistData data, Bundle extras) { - super.onProvideAssistData(data, extras); + public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) { + super.onProvideAssistStructure(structure, extras); final boolean isPassword = hasPasswordTransformationMethod(); if (!isPassword) { - data.setText(getText(), getSelectionStart(), getSelectionEnd()); + structure.setText(getText(), getSelectionStart(), getSelectionEnd()); + structure.setTextPaint(mTextPaint); } - data.setHint(getHint()); + structure.setHint(getHint()); } /** @hide */ diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 2bd607c..0350d61 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.CompressFormat; import android.net.Uri; +import android.util.ArrayMap; import android.util.Base64; import android.util.Xml; @@ -822,7 +823,36 @@ public class XmlUtils { int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, false); + map.put(name[0], val); + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return map; + } + throw new XmlPullParserException( + "Expected " + endTag + " end tag at: " + parser.getName()); + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException( + "Document ended before " + endTag + " end tag"); + } + + /** + * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap. + * @hide + */ + public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag, + String[] name, ReadMapCallback callback) + throws XmlPullParserException, java.io.IOException + { + ArrayMap<String, Object> map = new ArrayMap<>(); + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + Object val = readThisValueXml(parser, name, callback, true); map.put(name[0], val); } else if (eventType == parser.END_TAG) { if (parser.getName().equals(endTag)) { @@ -854,7 +884,7 @@ public class XmlUtils { */ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { - return readThisListXml(parser, endTag, name, null); + return readThisListXml(parser, endTag, name, null, false); } /** @@ -872,14 +902,14 @@ public class XmlUtils { * @see #readListXml */ private static final ArrayList readThisListXml(XmlPullParser parser, String endTag, - String[] name, ReadMapCallback callback) + String[] name, ReadMapCallback callback, boolean arrayMap) throws XmlPullParserException, java.io.IOException { ArrayList list = new ArrayList(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, arrayMap); list.add(val); //System.out.println("Adding to list: " + val); } else if (eventType == parser.END_TAG) { @@ -915,7 +945,7 @@ public class XmlUtils { */ public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { - return readThisSetXml(parser, endTag, name, null); + return readThisSetXml(parser, endTag, name, null, false); } /** @@ -937,13 +967,14 @@ public class XmlUtils { * @hide */ private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name, - ReadMapCallback callback) throws XmlPullParserException, java.io.IOException { + ReadMapCallback callback, boolean arrayMap) + throws XmlPullParserException, java.io.IOException { HashSet set = new HashSet(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - Object val = readThisValueXml(parser, name, callback); + Object val = readThisValueXml(parser, name, callback, arrayMap); set.add(val); //System.out.println("Adding to set: " + val); } else if (eventType == parser.END_TAG) { @@ -1292,7 +1323,7 @@ public class XmlUtils { int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { - return readThisValueXml(parser, name, null); + return readThisValueXml(parser, name, null, false); } else if (eventType == parser.END_TAG) { throw new XmlPullParserException( "Unexpected end tag at: " + parser.getName()); @@ -1308,7 +1339,8 @@ public class XmlUtils { } private static final Object readThisValueXml(XmlPullParser parser, String[] name, - ReadMapCallback callback) throws XmlPullParserException, java.io.IOException { + ReadMapCallback callback, boolean arrayMap) + throws XmlPullParserException, java.io.IOException { final String valueName = parser.getAttributeValue(null, "name"); final String tagName = parser.getName(); @@ -1368,19 +1400,21 @@ public class XmlUtils { return res; } else if (tagName.equals("map")) { parser.next(); - res = readThisMapXml(parser, "map", name); + res = arrayMap + ? readThisArrayMapXml(parser, "map", name, callback) + : readThisMapXml(parser, "map", name, callback); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; } else if (tagName.equals("list")) { parser.next(); - res = readThisListXml(parser, "list", name); + res = readThisListXml(parser, "list", name, callback, arrayMap); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; } else if (tagName.equals("set")) { parser.next(); - res = readThisSetXml(parser, "set", name); + res = readThisSetXml(parser, "set", name, callback, arrayMap); name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c4122e8..b5ea214 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10149,16 +10149,13 @@ public final class ActivityManagerService extends ActivityManagerNative private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint, IResultReceiver receiver, int userHandle) { enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO, - "getAssistContextExtras()"); - PendingAssistExtras pae; - Bundle extras = new Bundle(); + "enqueueAssistContext()"); synchronized (this) { - ActivityRecord activity = getFocusedStack().mResumedActivity; + ActivityRecord activity = getFocusedStack().topActivity(); if (activity == null) { - Slog.w(TAG, "getAssistContextExtras failed: no resumed activity"); + Slog.w(TAG, "getAssistContextExtras failed: no top activity"); return null; } - extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName); if (activity.app == null || activity.app.thread == null) { Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity); return null; @@ -10167,6 +10164,10 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "getAssistContextExtras failed: request process same as " + activity); return null; } + PendingAssistExtras pae; + Bundle extras = new Bundle(); + extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName); + extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid); pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, userHandle); try { activity.app.thread.requestAssistContextExtras(activity.appToken, pae, diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index 663efe5..2b2b2ac 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -264,7 +264,7 @@ public class SearchManagerService extends ISearchManager.Stub { } @Override - public boolean launchAssistAction(int requestType, String hint, int userHandle) { + public boolean launchAssistAction(String hint, int userHandle) { ComponentName comp = getAssistIntent(userHandle); if (comp == null) { return false; @@ -274,7 +274,8 @@ public class SearchManagerService extends ISearchManager.Stub { Intent intent = new Intent(Intent.ACTION_ASSIST); intent.setComponent(comp); IActivityManager am = ActivityManagerNative.getDefault(); - return am.launchAssistIntent(intent, requestType, hint, userHandle); + return am.launchAssistIntent(intent, ActivityManager.ASSIST_CONTEXT_BASIC, hint, + userHandle); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index e2b47c3..30d97b9 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -16,12 +16,17 @@ package com.android.server.voiceinteraction; +import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.AssistContent; import android.app.IActivityManager; +import android.content.ClipData; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -53,6 +58,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { final int mCallingUid; final IActivityManager mAm; final IWindowManager mIWindowManager; + final IBinder mPermissionOwner; boolean mShown; Bundle mShowArgs; int mShowFlags; @@ -83,15 +89,9 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { public void send(int resultCode, Bundle resultData) throws RemoteException { synchronized (mLock) { if (mShown) { - if (mSession != null) { - try { - mSession.handleAssist(resultData); - } catch (RemoteException e) { - } - } else { - mHaveAssistData = true; - mAssistData = resultData; - } + mHaveAssistData = true; + mAssistData = resultData; + deliverAssistData(); } } } @@ -109,6 +109,14 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { mAm = ActivityManagerNative.getDefault(); mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); + IBinder permOwner = null; + try { + permOwner = mAm.newUriPermissionOwner("voicesession:" + + component.flattenToShortString()); + } catch (RemoteException e) { + Slog.w("voicesession", "AM dead", e); + } + mPermissionOwner = permOwner; mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE); mBindIntent.setComponent(mSessionComponentName); mBound = mContext.bindServiceAsUser(mBindIntent, this, @@ -138,7 +146,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { mShowFlags = flags; if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) { try { - mAm.requestAssistContextExtras(0, mAssistReceiver); + mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL, + mAssistReceiver); } catch (RemoteException e) { } } else { @@ -152,20 +161,91 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { mShowFlags = 0; } catch (RemoteException e) { } - if (mHaveAssistData) { - try { - mSession.handleAssist(mAssistData); - mAssistData = null; - mHaveAssistData = false; - } catch (RemoteException e) { - } - } + deliverAssistData(); } return true; } return false; } + void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) { + if (!"content".equals(uri.getScheme())) { + return; + } + long ident = Binder.clearCallingIdentity(); + try { + // This will throw SecurityException for us. + mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri), + mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid))); + // No security exception, do the grant. + int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser); + uri = ContentProvider.getUriWithoutUserId(uri); + mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg, + uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser); + } catch (RemoteException e) { + } catch (SecurityException e) { + Slog.w(TAG, "Can't propagate permission", e); + } finally { + Binder.restoreCallingIdentity(ident); + } + + } + + void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, + String destPkg) { + if (item.getUri() != null) { + grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg); + } + Intent intent = item.getIntent(); + if (intent != null && intent.getData() != null) { + grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg); + } + } + + void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, + String destPkg) { + final int N = data.getItemCount(); + for (int i=0; i<N; i++) { + grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg); + } + } + + void deliverAssistData() { + if (mSession == null || !mHaveAssistData) { + return; + } + if (mAssistData != null) { + int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1); + if (uid >= 0) { + Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT); + if (assistContext != null) { + AssistContent content = AssistContent.getAssistContent(assistContext); + if (content != null) { + Intent intent = content.getIntent(); + if (intent != null) { + ClipData data = intent.getClipData(); + if (data != null && Intent.isAccessUriMode(intent.getFlags())) { + grantClipDataPermissions(data, intent.getFlags(), uid, + mCallingUid, mSessionComponentName.getPackageName()); + } + } + ClipData data = content.getClipData(); + if (data != null) { + grantClipDataPermissions(data, Intent.FLAG_GRANT_READ_URI_PERMISSION, + uid, mCallingUid, mSessionComponentName.getPackageName()); + } + } + } + } + } + try { + mSession.handleAssist(mAssistData); + mAssistData = null; + mHaveAssistData = false; + } catch (RemoteException e) { + } + } + public boolean hideLocked() { if (mBound) { if (mShown) { @@ -180,6 +260,13 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { } catch (RemoteException e) { } } + try { + mAm.revokeUriPermissionFromOwner(mPermissionOwner, null, + Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, + mUser); + } catch (RemoteException e) { + } } if (mFullyBound) { mContext.unbindService(mFullConnection); diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java index 6a99351..d0c5e36 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistProxyActivity.java @@ -28,7 +28,6 @@ public class AssistProxyActivity extends Activity { finish(); Intent intent = new Intent(this, MainInteractionService.class); intent.setAction(Intent.ACTION_ASSIST); - intent.putExtras(getIntent()); startService(intent); } } diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java index d35bc5c..bae19a6 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java @@ -17,7 +17,7 @@ package com.android.test.voiceinteraction; import android.annotation.Nullable; -import android.app.AssistData; +import android.app.AssistStructure; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; @@ -31,7 +31,7 @@ import java.util.ArrayList; public class AssistVisualizer extends View { static final String TAG = "AssistVisualizer"; - AssistData mAssistData; + AssistStructure mAssistStructure; final Paint mFramePaint = new Paint(); final ArrayList<Rect> mTextRects = new ArrayList<>(); final int[] mTmpLocation = new int[2]; @@ -44,25 +44,25 @@ public class AssistVisualizer extends View { mFramePaint.setStrokeWidth(0); } - public void setAssistData(AssistData ad) { - mAssistData = ad; + public void setAssistStructure(AssistStructure as) { + mAssistStructure = as; mTextRects.clear(); - final int N = ad.getWindowCount(); + final int N = as.getWindowCount(); if (N > 0) { - AssistData.ViewNode window = new AssistData.ViewNode(); + AssistStructure.ViewNode window = new AssistStructure.ViewNode(); for (int i=0; i<N; i++) { - ad.getWindowAt(i, window); + as.getWindowAt(i, window); buildTextRects(window, 0, 0); } } } public void clearAssistData() { - mAssistData = null; + mAssistStructure = null; mTextRects.clear(); } - void buildTextRects(AssistData.ViewNode root, int parentLeft, int parentTop) { + void buildTextRects(AssistStructure.ViewNode root, int parentLeft, int parentTop) { if (root.getVisibility() != View.VISIBLE) { return; } @@ -78,7 +78,7 @@ public class AssistVisualizer extends View { if (N > 0) { left -= root.getScrollX(); top -= root.getScrollY(); - AssistData.ViewNode child = new AssistData.ViewNode(); + AssistStructure.ViewNode child = new AssistStructure.ViewNode(); for (int i=0; i<N; i++) { root.getChildAt(i, child); buildTextRects(child, left, top); diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index 2cab3ea..722b0de 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -79,12 +79,7 @@ public class MainInteractionService extends VoiceInteractionService { Bundle args = new Bundle(); args.putParcelable("intent", new Intent(this, TestInteractionActivity.class)); args.putBundle("assist", intent.getExtras()); - Bundle assistContext = intent.getBundleExtra(Intent.EXTRA_ASSIST_CONTEXT); - int startFlags = 0; - if (assistContext == null) { - startFlags |= START_WITH_ASSIST; - } - startSession(args, startFlags); + startSession(args, START_WITH_ASSIST); } else { Log.w(TAG, "Not starting -- not current voice interaction service"); } diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java index 1e30aff..bcfc6f4 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java @@ -16,7 +16,8 @@ package com.android.test.voiceinteraction; -import android.app.AssistData; +import android.app.AssistContent; +import android.app.AssistStructure; import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -41,7 +42,7 @@ public class MainInteractionSession extends VoiceInteractionSession Button mCompleteButton; Button mAbortButton; - AssistData mAssistData; + AssistStructure mAssistStructure; static final int STATE_IDLE = 0; static final int STATE_LAUNCHING = 1; @@ -68,8 +69,9 @@ public class MainInteractionSession extends VoiceInteractionSession super.onShow(args, showFlags); mState = STATE_IDLE; mStartIntent = args.getParcelable("intent"); - Bundle assist = args.getBundle("assist"); - parseAssistData(assist); + if (mAssistVisualizer != null) { + mAssistVisualizer.clearAssistData(); + } updateState(); } @@ -87,8 +89,8 @@ public class MainInteractionSession extends VoiceInteractionSession public View onCreateContentView() { mContentView = getLayoutInflater().inflate(R.layout.voice_interaction_session, null); mAssistVisualizer = (AssistVisualizer)mContentView.findViewById(R.id.assist_visualizer); - if (mAssistData != null) { - mAssistVisualizer.setAssistData(mAssistData); + if (mAssistStructure != null) { + mAssistVisualizer.setAssistStructure(mAssistStructure); } mTopContent = mContentView.findViewById(R.id.top_content); mBottomContent = mContentView.findViewById(R.id.bottom_content); @@ -117,10 +119,17 @@ public class MainInteractionSession extends VoiceInteractionSession if (assistBundle != null) { Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT); if (assistContext != null) { - mAssistData = AssistData.getAssistData(assistContext); - mAssistData.dump(); - if (mAssistVisualizer != null) { - mAssistVisualizer.setAssistData(mAssistData); + mAssistStructure = AssistStructure.getAssistStructure(assistContext); + if (mAssistStructure != null) { + mAssistStructure.dump(); + if (mAssistVisualizer != null) { + mAssistVisualizer.setAssistStructure(mAssistStructure); + } + } + AssistContent content = AssistContent.getAssistContent(assistContext); + if (content != null) { + Log.i(TAG, "Assist intent: " + content.getIntent()); + Log.i(TAG, "Assist clipdata: " + content.getClipData()); } return; } |